diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6f1727a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.js linguist-language=java +*.html linguist-language=java +*.css linguist-language=java +*.md linguist-language=java \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97ede01 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.interviewGuide +/interviewGuide \ No newline at end of file diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/Podfile b/Podfile deleted file mode 100644 index 08d6214..0000000 --- a/Podfile +++ /dev/null @@ -1,28 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '8.0' -# Uncomment this line if you're using Swift -# use_frameworks! - -target 'TTNews' do - -pod 'MJRefresh' -pod 'MJExtension' -pod 'SDWebImage' -pod 'pop' -pod 'SVProgressHUD' -pod 'DACircularProgress' -pod 'FMDB' -pod 'NJKWebViewProgress' -pod 'DKNightVersion' -pod 'AFNetworking' - -end - -target 'TTNewsTests' do - -end - -target 'TTNewsUITests' do - -end - diff --git a/Podfile.lock b/Podfile.lock deleted file mode 100644 index 736bf1d..0000000 --- a/Podfile.lock +++ /dev/null @@ -1,71 +0,0 @@ -PODS: - - AFNetworking (3.1.0): - - AFNetworking/NSURLSession (= 3.1.0) - - AFNetworking/Reachability (= 3.1.0) - - AFNetworking/Security (= 3.1.0) - - AFNetworking/Serialization (= 3.1.0) - - AFNetworking/UIKit (= 3.1.0) - - AFNetworking/NSURLSession (3.1.0): - - AFNetworking/Reachability - - AFNetworking/Security - - AFNetworking/Serialization - - AFNetworking/Reachability (3.1.0) - - AFNetworking/Security (3.1.0) - - AFNetworking/Serialization (3.1.0) - - AFNetworking/UIKit (3.1.0): - - AFNetworking/NSURLSession - - DACircularProgress (2.3.1) - - DKNightVersion (2.3.0): - - DKNightVersion/Core (= 2.3.0) - - DKNightVersion/CoreAnimation (= 2.3.0) - - DKNightVersion/UIKit (= 2.3.0) - - DKNightVersion/Core (2.3.0): - - DKNightVersion/Core/DeallocBlockExecutor (= 2.3.0) - - DKNightVersion/Core/extobjc (= 2.3.0) - - DKNightVersion/Core/DeallocBlockExecutor (2.3.0) - - DKNightVersion/Core/extobjc (2.3.0) - - DKNightVersion/CoreAnimation (2.3.0): - - DKNightVersion/Core - - DKNightVersion/UIKit (2.3.0): - - DKNightVersion/Core - - FMDB (2.6.2): - - FMDB/standard (= 2.6.2) - - FMDB/standard (2.6.2) - - MJExtension (3.0.10) - - MJRefresh (3.1.0) - - NJKWebViewProgress (0.2.3): - - NJKWebViewProgress/Core (= 0.2.3) - - NJKWebViewProgress/ProgressView (= 0.2.3) - - NJKWebViewProgress/Core (0.2.3) - - NJKWebViewProgress/ProgressView (0.2.3) - - pop (1.0.9) - - SDWebImage (3.7.5): - - SDWebImage/Core (= 3.7.5) - - SDWebImage/Core (3.7.5) - - SVProgressHUD (2.0.3) - -DEPENDENCIES: - - AFNetworking - - DACircularProgress - - DKNightVersion - - FMDB - - MJExtension - - MJRefresh - - NJKWebViewProgress - - pop - - SDWebImage - - SVProgressHUD - -SPEC CHECKSUMS: - AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 - DACircularProgress: 4dd437c0fc3da5161cb289e07ac449493d41db71 - DKNightVersion: 459438cbfd14edb9e41a777ff14b18e5661b9269 - FMDB: 854a0341b4726e53276f2a8996f06f1b80f9259a - MJExtension: d86aacb740c87519d20e3cca55b6fa4be6cc7548 - MJRefresh: 743e6404967d1c2c688472ea3ecfde247d872db4 - NJKWebViewProgress: f481fd424cb5ecc27eacae11b5397db92fba9a4d - pop: f667631a5108a2e60d9e8797c9b32ddaf2080bce - SDWebImage: 69c6303e3348fba97e03f65d65d4fbc26740f461 - SVProgressHUD: b0830714205bea1317ea1a2ebc71e5633af334d4 - -COCOAPODS: 0.39.0 diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h deleted file mode 100644 index 5ce279a..0000000 --- a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h +++ /dev/null @@ -1,295 +0,0 @@ -// AFHTTPSessionManager.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import -#if !TARGET_OS_WATCH -#import -#endif -#import - -#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV -#import -#else -#import -#endif - -#import "AFURLSessionManager.h" - -/** - `AFHTTPSessionManager` is a subclass of `AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths. - - ## Subclassing Notes - - Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. - - For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect. - - ## Methods to Override - - To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:completionHandler:`. - - ## Serialization - - Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. - - Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` - - ## URL Construction Using Relative Paths - - For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`. - - Below are a few examples of how `baseURL` and relative paths interact: - - NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; - [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo - [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz - [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo - [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo - [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/ - [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ - - Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash. - - @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance. - */ - -NS_ASSUME_NONNULL_BEGIN - -@interface AFHTTPSessionManager : AFURLSessionManager - -/** - The URL used to construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods. - */ -@property (readonly, nonatomic, strong, nullable) NSURL *baseURL; - -/** - Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. - - @warning `requestSerializer` must not be `nil`. - */ -@property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; - -/** - Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. - - @warning `responseSerializer` must not be `nil`. - */ -@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; - -///--------------------- -/// @name Initialization -///--------------------- - -/** - Creates and returns an `AFHTTPSessionManager` object. - */ -+ (instancetype)manager; - -/** - Initializes an `AFHTTPSessionManager` object with the specified base URL. - - @param url The base URL for the HTTP client. - - @return The newly-initialized HTTP client - */ -- (instancetype)initWithBaseURL:(nullable NSURL *)url; - -/** - Initializes an `AFHTTPSessionManager` object with the specified base URL. - - This is the designated initializer. - - @param url The base URL for the HTTP client. - @param configuration The configuration used to create the managed session. - - @return The newly-initialized HTTP client - */ -- (instancetype)initWithBaseURL:(nullable NSURL *)url - sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; - -///--------------------------- -/// @name Making HTTP Requests -///--------------------------- - -/** - Creates and runs an `NSURLSessionDataTask` with a `GET` request. - - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded according to the client request serializer. - @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. - @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. - - @see -dataTaskWithRequest:completionHandler: - */ -- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString - parameters:(nullable id)parameters - success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE; - - -/** - Creates and runs an `NSURLSessionDataTask` with a `GET` request. - - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded according to the client request serializer. - @param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. - @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. - @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. - - @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: - */ -- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString - parameters:(nullable id)parameters - progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress - success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; - -/** - Creates and runs an `NSURLSessionDataTask` with a `HEAD` request. - - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded according to the client request serializer. - @param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task. - @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. - - @see -dataTaskWithRequest:completionHandler: - */ -- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString - parameters:(nullable id)parameters - success:(nullable void (^)(NSURLSessionDataTask *task))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; - -/** - Creates and runs an `NSURLSessionDataTask` with a `POST` request. - - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded according to the client request serializer. - @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. - @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. - - @see -dataTaskWithRequest:completionHandler: - */ -- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString - parameters:(nullable id)parameters - success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE; - -/** - Creates and runs an `NSURLSessionDataTask` with a `POST` request. - - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded according to the client request serializer. - @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. - @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. - @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. - - @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: - */ -- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString - parameters:(nullable id)parameters - progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress - success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; - -/** - Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request. - - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded according to the client request serializer. - @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. - @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. - @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. - - @see -dataTaskWithRequest:completionHandler: - */ -- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString - parameters:(nullable id)parameters - constructingBodyWithBlock:(nullable void (^)(id formData))block - success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE; - -/** - Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request. - - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded according to the client request serializer. - @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. - @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. - @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. - @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. - - @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: - */ -- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString - parameters:(nullable id)parameters - constructingBodyWithBlock:(nullable void (^)(id formData))block - progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress - success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; - -/** - Creates and runs an `NSURLSessionDataTask` with a `PUT` request. - - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded according to the client request serializer. - @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. - @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. - - @see -dataTaskWithRequest:completionHandler: - */ -- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString - parameters:(nullable id)parameters - success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; - -/** - Creates and runs an `NSURLSessionDataTask` with a `PATCH` request. - - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded according to the client request serializer. - @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. - @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. - - @see -dataTaskWithRequest:completionHandler: - */ -- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString - parameters:(nullable id)parameters - success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; - -/** - Creates and runs an `NSURLSessionDataTask` with a `DELETE` request. - - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded according to the client request serializer. - @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. - @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. - - @see -dataTaskWithRequest:completionHandler: - */ -- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString - parameters:(nullable id)parameters - success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m deleted file mode 100644 index 2591070..0000000 --- a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m +++ /dev/null @@ -1,361 +0,0 @@ -// AFHTTPSessionManager.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "AFHTTPSessionManager.h" - -#import "AFURLRequestSerialization.h" -#import "AFURLResponseSerialization.h" - -#import -#import -#import - -#import -#import -#import -#import -#import - -#if TARGET_OS_IOS || TARGET_OS_TV -#import -#elif TARGET_OS_WATCH -#import -#endif - -@interface AFHTTPSessionManager () -@property (readwrite, nonatomic, strong) NSURL *baseURL; -@end - -@implementation AFHTTPSessionManager -@dynamic responseSerializer; - -+ (instancetype)manager { - return [[[self class] alloc] initWithBaseURL:nil]; -} - -- (instancetype)init { - return [self initWithBaseURL:nil]; -} - -- (instancetype)initWithBaseURL:(NSURL *)url { - return [self initWithBaseURL:url sessionConfiguration:nil]; -} - -- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { - return [self initWithBaseURL:nil sessionConfiguration:configuration]; -} - -- (instancetype)initWithBaseURL:(NSURL *)url - sessionConfiguration:(NSURLSessionConfiguration *)configuration -{ - self = [super initWithSessionConfiguration:configuration]; - if (!self) { - return nil; - } - - // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected - if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { - url = [url URLByAppendingPathComponent:@""]; - } - - self.baseURL = url; - - self.requestSerializer = [AFHTTPRequestSerializer serializer]; - self.responseSerializer = [AFJSONResponseSerializer serializer]; - - return self; -} - -#pragma mark - - -- (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { - NSParameterAssert(requestSerializer); - - _requestSerializer = requestSerializer; -} - -- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { - NSParameterAssert(responseSerializer); - - [super setResponseSerializer:responseSerializer]; -} - -#pragma mark - - -- (NSURLSessionDataTask *)GET:(NSString *)URLString - parameters:(id)parameters - success:(void (^)(NSURLSessionDataTask *task, id responseObject))success - failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure -{ - - return [self GET:URLString parameters:parameters progress:nil success:success failure:failure]; -} - -- (NSURLSessionDataTask *)GET:(NSString *)URLString - parameters:(id)parameters - progress:(void (^)(NSProgress * _Nonnull))downloadProgress - success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success - failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure -{ - - NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" - URLString:URLString - parameters:parameters - uploadProgress:nil - downloadProgress:downloadProgress - success:success - failure:failure]; - - [dataTask resume]; - - return dataTask; -} - -- (NSURLSessionDataTask *)HEAD:(NSString *)URLString - parameters:(id)parameters - success:(void (^)(NSURLSessionDataTask *task))success - failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure -{ - NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:^(NSURLSessionDataTask *task, __unused id responseObject) { - if (success) { - success(task); - } - } failure:failure]; - - [dataTask resume]; - - return dataTask; -} - -- (NSURLSessionDataTask *)POST:(NSString *)URLString - parameters:(id)parameters - success:(void (^)(NSURLSessionDataTask *task, id responseObject))success - failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure -{ - return [self POST:URLString parameters:parameters progress:nil success:success failure:failure]; -} - -- (NSURLSessionDataTask *)POST:(NSString *)URLString - parameters:(id)parameters - progress:(void (^)(NSProgress * _Nonnull))uploadProgress - success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success - failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure -{ - NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure]; - - [dataTask resume]; - - return dataTask; -} - -- (NSURLSessionDataTask *)POST:(NSString *)URLString - parameters:(nullable id)parameters - constructingBodyWithBlock:(nullable void (^)(id _Nonnull))block - success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success - failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure -{ - return [self POST:URLString parameters:parameters constructingBodyWithBlock:block progress:nil success:success failure:failure]; -} - -- (NSURLSessionDataTask *)POST:(NSString *)URLString - parameters:(id)parameters - constructingBodyWithBlock:(void (^)(id formData))block - progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress - success:(void (^)(NSURLSessionDataTask *task, id responseObject))success - failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure -{ - NSError *serializationError = nil; - NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; - if (serializationError) { - if (failure) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" - dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ - failure(nil, serializationError); - }); -#pragma clang diagnostic pop - } - - return nil; - } - - __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { - if (error) { - if (failure) { - failure(task, error); - } - } else { - if (success) { - success(task, responseObject); - } - } - }]; - - [task resume]; - - return task; -} - -- (NSURLSessionDataTask *)PUT:(NSString *)URLString - parameters:(id)parameters - success:(void (^)(NSURLSessionDataTask *task, id responseObject))success - failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure -{ - NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure]; - - [dataTask resume]; - - return dataTask; -} - -- (NSURLSessionDataTask *)PATCH:(NSString *)URLString - parameters:(id)parameters - success:(void (^)(NSURLSessionDataTask *task, id responseObject))success - failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure -{ - NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure]; - - [dataTask resume]; - - return dataTask; -} - -- (NSURLSessionDataTask *)DELETE:(NSString *)URLString - parameters:(id)parameters - success:(void (^)(NSURLSessionDataTask *task, id responseObject))success - failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure -{ - NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure]; - - [dataTask resume]; - - return dataTask; -} - -- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method - URLString:(NSString *)URLString - parameters:(id)parameters - uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress - downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress - success:(void (^)(NSURLSessionDataTask *, id))success - failure:(void (^)(NSURLSessionDataTask *, NSError *))failure -{ - NSError *serializationError = nil; - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; - if (serializationError) { - if (failure) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" - dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ - failure(nil, serializationError); - }); -#pragma clang diagnostic pop - } - - return nil; - } - - __block NSURLSessionDataTask *dataTask = nil; - dataTask = [self dataTaskWithRequest:request - uploadProgress:uploadProgress - downloadProgress:downloadProgress - completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { - if (error) { - if (failure) { - failure(dataTask, error); - } - } else { - if (success) { - success(dataTask, responseObject); - } - } - }]; - - return dataTask; -} - -#pragma mark - NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue]; -} - -#pragma mark - NSSecureCoding - -+ (BOOL)supportsSecureCoding { - return YES; -} - -- (instancetype)initWithCoder:(NSCoder *)decoder { - NSURL *baseURL = [decoder decodeObjectOfClass:[NSURL class] forKey:NSStringFromSelector(@selector(baseURL))]; - NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; - if (!configuration) { - NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"]; - if (configurationIdentifier) { -#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100) - configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier]; -#else - configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier]; -#endif - } - } - - self = [self initWithBaseURL:baseURL sessionConfiguration:configuration]; - if (!self) { - return nil; - } - - self.requestSerializer = [decoder decodeObjectOfClass:[AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))]; - self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; - AFSecurityPolicy *decodedPolicy = [decoder decodeObjectOfClass:[AFSecurityPolicy class] forKey:NSStringFromSelector(@selector(securityPolicy))]; - if (decodedPolicy) { - self.securityPolicy = decodedPolicy; - } - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [super encodeWithCoder:coder]; - - [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; - if ([self.session.configuration conformsToProtocol:@protocol(NSCoding)]) { - [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; - } else { - [coder encodeObject:self.session.configuration.identifier forKey:@"identifier"]; - } - [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; - [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; - [coder encodeObject:self.securityPolicy forKey:NSStringFromSelector(@selector(securityPolicy))]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration]; - - HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; - HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; - HTTPClient.securityPolicy = [self.securityPolicy copyWithZone:zone]; - return HTTPClient; -} - -@end diff --git a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h deleted file mode 100644 index 0feb18d..0000000 --- a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h +++ /dev/null @@ -1,206 +0,0 @@ -// AFNetworkReachabilityManager.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#if !TARGET_OS_WATCH -#import - -typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { - AFNetworkReachabilityStatusUnknown = -1, - AFNetworkReachabilityStatusNotReachable = 0, - AFNetworkReachabilityStatusReachableViaWWAN = 1, - AFNetworkReachabilityStatusReachableViaWiFi = 2, -}; - -NS_ASSUME_NONNULL_BEGIN - -/** - `AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. - - Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability. - - See Apple's Reachability Sample Code ( https://developer.apple.com/library/ios/samplecode/reachability/ ) - - @warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined. - */ -@interface AFNetworkReachabilityManager : NSObject - -/** - The current network reachability status. - */ -@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; - -/** - Whether or not the network is currently reachable. - */ -@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable; - -/** - Whether or not the network is currently reachable via WWAN. - */ -@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN; - -/** - Whether or not the network is currently reachable via WiFi. - */ -@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi; - -///--------------------- -/// @name Initialization -///--------------------- - -/** - Returns the shared network reachability manager. - */ -+ (instancetype)sharedManager; - -/** - Creates and returns a network reachability manager with the default socket address. - - @return An initialized network reachability manager, actively monitoring the default socket address. - */ -+ (instancetype)manager; - -/** - Creates and returns a network reachability manager for the specified domain. - - @param domain The domain used to evaluate network reachability. - - @return An initialized network reachability manager, actively monitoring the specified domain. - */ -+ (instancetype)managerForDomain:(NSString *)domain; - -/** - Creates and returns a network reachability manager for the socket address. - - @param address The socket address (`sockaddr_in6`) used to evaluate network reachability. - - @return An initialized network reachability manager, actively monitoring the specified socket address. - */ -+ (instancetype)managerForAddress:(const void *)address; - -/** - Initializes an instance of a network reachability manager from the specified reachability object. - - @param reachability The reachability object to monitor. - - @return An initialized network reachability manager, actively monitoring the specified reachability. - */ -- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER; - -///-------------------------------------------------- -/// @name Starting & Stopping Reachability Monitoring -///-------------------------------------------------- - -/** - Starts monitoring for changes in network reachability status. - */ -- (void)startMonitoring; - -/** - Stops monitoring for changes in network reachability status. - */ -- (void)stopMonitoring; - -///------------------------------------------------- -/// @name Getting Localized Reachability Description -///------------------------------------------------- - -/** - Returns a localized string representation of the current network reachability status. - */ -- (NSString *)localizedNetworkReachabilityStatusString; - -///--------------------------------------------------- -/// @name Setting Network Reachability Change Callback -///--------------------------------------------------- - -/** - Sets a callback to be executed when the network availability of the `baseURL` host changes. - - @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`. - */ -- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block; - -@end - -///---------------- -/// @name Constants -///---------------- - -/** - ## Network Reachability - - The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses. - - enum { - AFNetworkReachabilityStatusUnknown, - AFNetworkReachabilityStatusNotReachable, - AFNetworkReachabilityStatusReachableViaWWAN, - AFNetworkReachabilityStatusReachableViaWiFi, - } - - `AFNetworkReachabilityStatusUnknown` - The `baseURL` host reachability is not known. - - `AFNetworkReachabilityStatusNotReachable` - The `baseURL` host cannot be reached. - - `AFNetworkReachabilityStatusReachableViaWWAN` - The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS. - - `AFNetworkReachabilityStatusReachableViaWiFi` - The `baseURL` host can be reached via a Wi-Fi connection. - - ### Keys for Notification UserInfo Dictionary - - Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification. - - `AFNetworkingReachabilityNotificationStatusItem` - A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification. - The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status. - */ - -///-------------------- -/// @name Notifications -///-------------------- - -/** - Posted when network reachability changes. - This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability. - - @warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (`Prefix.pch`). - */ -FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification; -FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem; - -///-------------------- -/// @name Functions -///-------------------- - -/** - Returns a localized string representation of an `AFNetworkReachabilityStatus` value. - */ -FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status); - -NS_ASSUME_NONNULL_END -#endif diff --git a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m deleted file mode 100644 index d458364..0000000 --- a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m +++ /dev/null @@ -1,263 +0,0 @@ -// AFNetworkReachabilityManager.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "AFNetworkReachabilityManager.h" -#if !TARGET_OS_WATCH - -#import -#import -#import -#import -#import - -NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change"; -NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem"; - -typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status); - -NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) { - switch (status) { - case AFNetworkReachabilityStatusNotReachable: - return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil); - case AFNetworkReachabilityStatusReachableViaWWAN: - return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil); - case AFNetworkReachabilityStatusReachableViaWiFi: - return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil); - case AFNetworkReachabilityStatusUnknown: - default: - return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil); - } -} - -static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { - BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); - BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); - BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); - BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); - BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); - - AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; - if (isNetworkReachable == NO) { - status = AFNetworkReachabilityStatusNotReachable; - } -#if TARGET_OS_IPHONE - else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { - status = AFNetworkReachabilityStatusReachableViaWWAN; - } -#endif - else { - status = AFNetworkReachabilityStatusReachableViaWiFi; - } - - return status; -} - -/** - * Queue a status change notification for the main thread. - * - * This is done to ensure that the notifications are received in the same order - * as they are sent. If notifications are sent directly, it is possible that - * a queued notification (for an earlier status condition) is processed after - * the later update, resulting in the listener being left in the wrong state. - */ -static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) { - AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); - dispatch_async(dispatch_get_main_queue(), ^{ - if (block) { - block(status); - } - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; - [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; - }); -} - -static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { - AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info); -} - - -static const void * AFNetworkReachabilityRetainCallback(const void *info) { - return Block_copy(info); -} - -static void AFNetworkReachabilityReleaseCallback(const void *info) { - if (info) { - Block_release(info); - } -} - -@interface AFNetworkReachabilityManager () -@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability; -@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; -@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; -@end - -@implementation AFNetworkReachabilityManager - -+ (instancetype)sharedManager { - static AFNetworkReachabilityManager *_sharedManager = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _sharedManager = [self manager]; - }); - - return _sharedManager; -} - -+ (instancetype)managerForDomain:(NSString *)domain { - SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]); - - AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; - - CFRelease(reachability); - - return manager; -} - -+ (instancetype)managerForAddress:(const void *)address { - SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); - AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; - - CFRelease(reachability); - - return manager; -} - -+ (instancetype)manager -{ -#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) - struct sockaddr_in6 address; - bzero(&address, sizeof(address)); - address.sin6_len = sizeof(address); - address.sin6_family = AF_INET6; -#else - struct sockaddr_in address; - bzero(&address, sizeof(address)); - address.sin_len = sizeof(address); - address.sin_family = AF_INET; -#endif - return [self managerForAddress:&address]; -} - -- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { - self = [super init]; - if (!self) { - return nil; - } - - _networkReachability = CFRetain(reachability); - self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; - - return self; -} - -- (instancetype)init NS_UNAVAILABLE -{ - return nil; -} - -- (void)dealloc { - [self stopMonitoring]; - - if (_networkReachability != NULL) { - CFRelease(_networkReachability); - } -} - -#pragma mark - - -- (BOOL)isReachable { - return [self isReachableViaWWAN] || [self isReachableViaWiFi]; -} - -- (BOOL)isReachableViaWWAN { - return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN; -} - -- (BOOL)isReachableViaWiFi { - return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi; -} - -#pragma mark - - -- (void)startMonitoring { - [self stopMonitoring]; - - if (!self.networkReachability) { - return; - } - - __weak __typeof(self)weakSelf = self; - AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { - __strong __typeof(weakSelf)strongSelf = weakSelf; - - strongSelf.networkReachabilityStatus = status; - if (strongSelf.networkReachabilityStatusBlock) { - strongSelf.networkReachabilityStatusBlock(status); - } - - }; - - SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; - SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); - SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ - SCNetworkReachabilityFlags flags; - if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) { - AFPostReachabilityStatusChange(flags, callback); - } - }); -} - -- (void)stopMonitoring { - if (!self.networkReachability) { - return; - } - - SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); -} - -#pragma mark - - -- (NSString *)localizedNetworkReachabilityStatusString { - return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus); -} - -#pragma mark - - -- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block { - self.networkReachabilityStatusBlock = block; -} - -#pragma mark - NSKeyValueObserving - -+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { - if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { - return [NSSet setWithObject:@"networkReachabilityStatus"]; - } - - return [super keyPathsForValuesAffectingValueForKey:key]; -} - -@end -#endif diff --git a/Pods/AFNetworking/AFNetworking/AFNetworking.h b/Pods/AFNetworking/AFNetworking/AFNetworking.h deleted file mode 100644 index e2fb2f4..0000000 --- a/Pods/AFNetworking/AFNetworking/AFNetworking.h +++ /dev/null @@ -1,41 +0,0 @@ -// AFNetworking.h -// -// Copyright (c) 2013 AFNetworking (http://afnetworking.com/) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import -#import -#import - -#ifndef _AFNETWORKING_ - #define _AFNETWORKING_ - - #import "AFURLRequestSerialization.h" - #import "AFURLResponseSerialization.h" - #import "AFSecurityPolicy.h" - -#if !TARGET_OS_WATCH - #import "AFNetworkReachabilityManager.h" -#endif - - #import "AFURLSessionManager.h" - #import "AFHTTPSessionManager.h" - -#endif /* _AFNETWORKING_ */ diff --git a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h deleted file mode 100644 index c005efa..0000000 --- a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h +++ /dev/null @@ -1,154 +0,0 @@ -// AFSecurityPolicy.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import -#import - -typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { - AFSSLPinningModeNone, - AFSSLPinningModePublicKey, - AFSSLPinningModeCertificate, -}; - -/** - `AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. - - Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. - */ - -NS_ASSUME_NONNULL_BEGIN - -@interface AFSecurityPolicy : NSObject - -/** - The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`. - */ -@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode; - -/** - The certificates used to evaluate server trust according to the SSL pinning mode. - - By default, this property is set to any (`.cer`) certificates included in the target compiling AFNetworking. Note that if you are using AFNetworking as embedded framework, no certificates will be pinned by default. Use `certificatesInBundle` to load certificates from your target, and then create a new policy by calling `policyWithPinningMode:withPinnedCertificates`. - - Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches. - */ -@property (nonatomic, strong, nullable) NSSet *pinnedCertificates; - -/** - Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`. - */ -@property (nonatomic, assign) BOOL allowInvalidCertificates; - -/** - Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`. - */ -@property (nonatomic, assign) BOOL validatesDomainName; - -///----------------------------------------- -/// @name Getting Certificates from the Bundle -///----------------------------------------- - -/** - Returns any certificates included in the bundle. If you are using AFNetworking as an embedded framework, you must use this method to find the certificates you have included in your app bundle, and use them when creating your security policy by calling `policyWithPinningMode:withPinnedCertificates`. - - @return The certificates included in the given bundle. - */ -+ (NSSet *)certificatesInBundle:(NSBundle *)bundle; - -///----------------------------------------- -/// @name Getting Specific Security Policies -///----------------------------------------- - -/** - Returns the shared default security policy, which does not allow invalid certificates, validates domain name, and does not validate against pinned certificates or public keys. - - @return The default security policy. - */ -+ (instancetype)defaultPolicy; - -///--------------------- -/// @name Initialization -///--------------------- - -/** - Creates and returns a security policy with the specified pinning mode. - - @param pinningMode The SSL pinning mode. - - @return A new security policy. - */ -+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode; - -/** - Creates and returns a security policy with the specified pinning mode. - - @param pinningMode The SSL pinning mode. - @param pinnedCertificates The certificates to pin against. - - @return A new security policy. - */ -+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates; - -///------------------------------ -/// @name Evaluating Server Trust -///------------------------------ - -/** - Whether or not the specified server trust should be accepted, based on the security policy. - - This method should be used when responding to an authentication challenge from a server. - - @param serverTrust The X.509 certificate trust of the server. - @param domain The domain of serverTrust. If `nil`, the domain will not be validated. - - @return Whether or not to trust the server. - */ -- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust - forDomain:(nullable NSString *)domain; - -@end - -NS_ASSUME_NONNULL_END - -///---------------- -/// @name Constants -///---------------- - -/** - ## SSL Pinning Modes - - The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes. - - enum { - AFSSLPinningModeNone, - AFSSLPinningModePublicKey, - AFSSLPinningModeCertificate, - } - - `AFSSLPinningModeNone` - Do not used pinned certificates to validate servers. - - `AFSSLPinningModePublicKey` - Validate host certificates against public keys of pinned certificates. - - `AFSSLPinningModeCertificate` - Validate host certificates against pinned certificates. -*/ diff --git a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m deleted file mode 100644 index ec81d37..0000000 --- a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m +++ /dev/null @@ -1,353 +0,0 @@ -// AFSecurityPolicy.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "AFSecurityPolicy.h" - -#import - -#if !TARGET_OS_IOS && !TARGET_OS_WATCH && !TARGET_OS_TV -static NSData * AFSecKeyGetData(SecKeyRef key) { - CFDataRef data = NULL; - - __Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out); - - return (__bridge_transfer NSData *)data; - -_out: - if (data) { - CFRelease(data); - } - - return nil; -} -#endif - -static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) { -#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV - return [(__bridge id)key1 isEqual:(__bridge id)key2]; -#else - return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)]; -#endif -} - -static id AFPublicKeyForCertificate(NSData *certificate) { - id allowedPublicKey = nil; - SecCertificateRef allowedCertificate; - SecCertificateRef allowedCertificates[1]; - CFArrayRef tempCertificates = nil; - SecPolicyRef policy = nil; - SecTrustRef allowedTrust = nil; - SecTrustResultType result; - - allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); - __Require_Quiet(allowedCertificate != NULL, _out); - - allowedCertificates[0] = allowedCertificate; - tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL); - - policy = SecPolicyCreateBasicX509(); - __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out); - __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); - - allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); - -_out: - if (allowedTrust) { - CFRelease(allowedTrust); - } - - if (policy) { - CFRelease(policy); - } - - if (tempCertificates) { - CFRelease(tempCertificates); - } - - if (allowedCertificate) { - CFRelease(allowedCertificate); - } - - return allowedPublicKey; -} - -static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { - BOOL isValid = NO; - SecTrustResultType result; - __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out); - - isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); - -_out: - return isValid; -} - -static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { - CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); - NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; - - for (CFIndex i = 0; i < certificateCount; i++) { - SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); - [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; - } - - return [NSArray arrayWithArray:trustChain]; -} - -static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { - SecPolicyRef policy = SecPolicyCreateBasicX509(); - CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); - NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; - for (CFIndex i = 0; i < certificateCount; i++) { - SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); - - SecCertificateRef someCertificates[] = {certificate}; - CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL); - - SecTrustRef trust; - __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out); - - SecTrustResultType result; - __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out); - - [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; - - _out: - if (trust) { - CFRelease(trust); - } - - if (certificates) { - CFRelease(certificates); - } - - continue; - } - CFRelease(policy); - - return [NSArray arrayWithArray:trustChain]; -} - -#pragma mark - - -@interface AFSecurityPolicy() -@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode; -@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys; -@end - -@implementation AFSecurityPolicy - -+ (NSSet *)certificatesInBundle:(NSBundle *)bundle { - NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; - - NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]]; - for (NSString *path in paths) { - NSData *certificateData = [NSData dataWithContentsOfFile:path]; - [certificates addObject:certificateData]; - } - - return [NSSet setWithSet:certificates]; -} - -+ (NSSet *)defaultPinnedCertificates { - static NSSet *_defaultPinnedCertificates = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - _defaultPinnedCertificates = [self certificatesInBundle:bundle]; - }); - - return _defaultPinnedCertificates; -} - -+ (instancetype)defaultPolicy { - AFSecurityPolicy *securityPolicy = [[self alloc] init]; - securityPolicy.SSLPinningMode = AFSSLPinningModeNone; - - return securityPolicy; -} - -+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode { - return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]]; -} - -+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates { - AFSecurityPolicy *securityPolicy = [[self alloc] init]; - securityPolicy.SSLPinningMode = pinningMode; - - [securityPolicy setPinnedCertificates:pinnedCertificates]; - - return securityPolicy; -} - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - - self.validatesDomainName = YES; - - return self; -} - -- (void)setPinnedCertificates:(NSSet *)pinnedCertificates { - _pinnedCertificates = pinnedCertificates; - - if (self.pinnedCertificates) { - NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]]; - for (NSData *certificate in self.pinnedCertificates) { - id publicKey = AFPublicKeyForCertificate(certificate); - if (!publicKey) { - continue; - } - [mutablePinnedPublicKeys addObject:publicKey]; - } - self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys]; - } else { - self.pinnedPublicKeys = nil; - } -} - -#pragma mark - - -- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust - forDomain:(NSString *)domain -{ - if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { - // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html - // According to the docs, you should only trust your provided certs for evaluation. - // Pinned certificates are added to the trust. Without pinned certificates, - // there is nothing to evaluate against. - // - // From Apple Docs: - // "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors). - // Instead, add your own (self-signed) CA certificate to the list of trusted anchors." - NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning."); - return NO; - } - - NSMutableArray *policies = [NSMutableArray array]; - if (self.validatesDomainName) { - [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)]; - } else { - [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()]; - } - - SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); - - if (self.SSLPinningMode == AFSSLPinningModeNone) { - return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust); - } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { - return NO; - } - - switch (self.SSLPinningMode) { - case AFSSLPinningModeNone: - default: - return NO; - case AFSSLPinningModeCertificate: { - NSMutableArray *pinnedCertificates = [NSMutableArray array]; - for (NSData *certificateData in self.pinnedCertificates) { - [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; - } - SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); - - if (!AFServerTrustIsValid(serverTrust)) { - return NO; - } - - // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA) - NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); - - for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) { - if ([self.pinnedCertificates containsObject:trustChainCertificate]) { - return YES; - } - } - - return NO; - } - case AFSSLPinningModePublicKey: { - NSUInteger trustedPublicKeyCount = 0; - NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); - - for (id trustChainPublicKey in publicKeys) { - for (id pinnedPublicKey in self.pinnedPublicKeys) { - if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { - trustedPublicKeyCount += 1; - } - } - } - return trustedPublicKeyCount > 0; - } - } - - return NO; -} - -#pragma mark - NSKeyValueObserving - -+ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys { - return [NSSet setWithObject:@"pinnedCertificates"]; -} - -#pragma mark - NSSecureCoding - -+ (BOOL)supportsSecureCoding { - return YES; -} - -- (instancetype)initWithCoder:(NSCoder *)decoder { - - self = [self init]; - if (!self) { - return nil; - } - - self.SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(SSLPinningMode))] unsignedIntegerValue]; - self.allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector(@selector(allowInvalidCertificates))]; - self.validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector(@selector(validatesDomainName))]; - self.pinnedCertificates = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(pinnedCertificates))]; - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:[NSNumber numberWithUnsignedInteger:self.SSLPinningMode] forKey:NSStringFromSelector(@selector(SSLPinningMode))]; - [coder encodeBool:self.allowInvalidCertificates forKey:NSStringFromSelector(@selector(allowInvalidCertificates))]; - [coder encodeBool:self.validatesDomainName forKey:NSStringFromSelector(@selector(validatesDomainName))]; - [coder encodeObject:self.pinnedCertificates forKey:NSStringFromSelector(@selector(pinnedCertificates))]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init]; - securityPolicy.SSLPinningMode = self.SSLPinningMode; - securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates; - securityPolicy.validatesDomainName = self.validatesDomainName; - securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone]; - - return securityPolicy; -} - -@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h deleted file mode 100644 index 694696b..0000000 --- a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h +++ /dev/null @@ -1,479 +0,0 @@ -// AFURLRequestSerialization.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import -#import - -#if TARGET_OS_IOS || TARGET_OS_TV -#import -#elif TARGET_OS_WATCH -#import -#endif - -NS_ASSUME_NONNULL_BEGIN - -/** - Returns a percent-escaped string following RFC 3986 for a query string key or value. - RFC 3986 states that the following characters are "reserved" characters. - - General Delimiters: ":", "#", "[", "]", "@", "?", "/" - - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" - - In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow - query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" - should be percent-escaped in the query string. - - @param string The string to be percent-escaped. - - @return The percent-escaped string. - */ -FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string); - -/** - A helper method to generate encoded url query parameters for appending to the end of a URL. - - @param parameters A dictionary of key/values to be encoded. - - @return A url encoded query string - */ -FOUNDATION_EXPORT NSString * AFQueryStringFromParameters(NSDictionary *parameters); - -/** - The `AFURLRequestSerialization` protocol is adopted by an object that encodes parameters for a specified HTTP requests. Request serializers may encode parameters as query strings, HTTP bodies, setting the appropriate HTTP header fields as necessary. - - For example, a JSON request serializer may set the HTTP body of the request to a JSON representation, and set the `Content-Type` HTTP header field value to `application/json`. - */ -@protocol AFURLRequestSerialization - -/** - Returns a request with the specified parameters encoded into a copy of the original request. - - @param request The original request. - @param parameters The parameters to be encoded. - @param error The error that occurred while attempting to encode the request parameters. - - @return A serialized request. - */ -- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request - withParameters:(nullable id)parameters - error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW; - -@end - -#pragma mark - - -/** - - */ -typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) { - AFHTTPRequestQueryStringDefaultStyle = 0, -}; - -@protocol AFMultipartFormData; - -/** - `AFHTTPRequestSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. - - Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPRequestSerializer` in order to ensure consistent default behavior. - */ -@interface AFHTTPRequestSerializer : NSObject - -/** - The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default. - */ -@property (nonatomic, assign) NSStringEncoding stringEncoding; - -/** - Whether created requests can use the device’s cellular radio (if present). `YES` by default. - - @see NSMutableURLRequest -setAllowsCellularAccess: - */ -@property (nonatomic, assign) BOOL allowsCellularAccess; - -/** - The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default. - - @see NSMutableURLRequest -setCachePolicy: - */ -@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy; - -/** - Whether created requests should use the default cookie handling. `YES` by default. - - @see NSMutableURLRequest -setHTTPShouldHandleCookies: - */ -@property (nonatomic, assign) BOOL HTTPShouldHandleCookies; - -/** - Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default - - @see NSMutableURLRequest -setHTTPShouldUsePipelining: - */ -@property (nonatomic, assign) BOOL HTTPShouldUsePipelining; - -/** - The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default. - - @see NSMutableURLRequest -setNetworkServiceType: - */ -@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType; - -/** - The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds. - - @see NSMutableURLRequest -setTimeoutInterval: - */ -@property (nonatomic, assign) NSTimeInterval timeoutInterval; - -///--------------------------------------- -/// @name Configuring HTTP Request Headers -///--------------------------------------- - -/** - Default HTTP header field values to be applied to serialized requests. By default, these include the following: - - - `Accept-Language` with the contents of `NSLocale +preferredLanguages` - - `User-Agent` with the contents of various bundle identifiers and OS designations - - @discussion To add or remove default request headers, use `setValue:forHTTPHeaderField:`. - */ -@property (readonly, nonatomic, strong) NSDictionary *HTTPRequestHeaders; - -/** - Creates and returns a serializer with default configuration. - */ -+ (instancetype)serializer; - -/** - Sets the value for the HTTP headers set in request objects made by the HTTP client. If `nil`, removes the existing value for that header. - - @param field The HTTP header to set a default value for - @param value The value set as default for the specified header, or `nil` - */ -- (void)setValue:(nullable NSString *)value -forHTTPHeaderField:(NSString *)field; - -/** - Returns the value for the HTTP headers set in the request serializer. - - @param field The HTTP header to retrieve the default value for - - @return The value set as default for the specified header, or `nil` - */ -- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field; - -/** - Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a basic authentication value with Base64-encoded username and password. This overwrites any existing value for this header. - - @param username The HTTP basic auth username - @param password The HTTP basic auth password - */ -- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username - password:(NSString *)password; - -/** - Clears any existing value for the "Authorization" HTTP header. - */ -- (void)clearAuthorizationHeader; - -///------------------------------------------------------- -/// @name Configuring Query String Parameter Serialization -///------------------------------------------------------- - -/** - HTTP methods for which serialized requests will encode parameters as a query string. `GET`, `HEAD`, and `DELETE` by default. - */ -@property (nonatomic, strong) NSSet *HTTPMethodsEncodingParametersInURI; - -/** - Set the method of query string serialization according to one of the pre-defined styles. - - @param style The serialization style. - - @see AFHTTPRequestQueryStringSerializationStyle - */ -- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style; - -/** - Set the a custom method of query string serialization according to the specified block. - - @param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request. - */ -- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block; - -///------------------------------- -/// @name Creating Request Objects -///------------------------------- - -/** - Creates an `NSMutableURLRequest` object with the specified HTTP method and URL string. - - If the HTTP method is `GET`, `HEAD`, or `DELETE`, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the `parameterEncoding` property, and set as the request body. - - @param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. This parameter must not be `nil`. - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body. - @param error The error that occurred while constructing the request. - - @return An `NSMutableURLRequest` object. - */ -- (NSMutableURLRequest *)requestWithMethod:(NSString *)method - URLString:(NSString *)URLString - parameters:(nullable id)parameters - error:(NSError * _Nullable __autoreleasing *)error; - -/** - Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2 - - Multipart form requests are automatically streamed, reading files directly from disk along with in-memory data in a single HTTP body. The resulting `NSMutableURLRequest` object has an `HTTPBodyStream` property, so refrain from setting `HTTPBodyStream` or `HTTPBody` on this request object, as it will clear out the multipart form body stream. - - @param method The HTTP method for the request. This parameter must not be `GET` or `HEAD`, or `nil`. - @param URLString The URL string used to create the request URL. - @param parameters The parameters to be encoded and set in the request HTTP body. - @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. - @param error The error that occurred while constructing the request. - - @return An `NSMutableURLRequest` object - */ -- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method - URLString:(NSString *)URLString - parameters:(nullable NSDictionary *)parameters - constructingBodyWithBlock:(nullable void (^)(id formData))block - error:(NSError * _Nullable __autoreleasing *)error; - -/** - Creates an `NSMutableURLRequest` by removing the `HTTPBodyStream` from a request, and asynchronously writing its contents into the specified file, invoking the completion handler when finished. - - @param request The multipart form request. The `HTTPBodyStream` property of `request` must not be `nil`. - @param fileURL The file URL to write multipart form contents to. - @param handler A handler block to execute. - - @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request. - - @see https://github.com/AFNetworking/AFNetworking/issues/1398 - */ -- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request - writingStreamContentsToFile:(NSURL *)fileURL - completionHandler:(nullable void (^)(NSError * _Nullable error))handler; - -@end - -#pragma mark - - -/** - The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`. - */ -@protocol AFMultipartFormData - -/** - Appends the HTTP header `Content-Disposition: file; filename=#{generated filename}; name=#{name}"` and `Content-Type: #{generated mimeType}`, followed by the encoded file data and the multipart form boundary. - - The filename and MIME type for this data in the form will be automatically generated, using the last path component of the `fileURL` and system associated MIME type for the `fileURL` extension, respectively. - - @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`. - @param name The name to be associated with the specified data. This parameter must not be `nil`. - @param error If an error occurs, upon return contains an `NSError` object that describes the problem. - - @return `YES` if the file data was successfully appended, otherwise `NO`. - */ -- (BOOL)appendPartWithFileURL:(NSURL *)fileURL - name:(NSString *)name - error:(NSError * _Nullable __autoreleasing *)error; - -/** - Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. - - @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`. - @param name The name to be associated with the specified data. This parameter must not be `nil`. - @param fileName The file name to be used in the `Content-Disposition` header. This parameter must not be `nil`. - @param mimeType The declared MIME type of the file data. This parameter must not be `nil`. - @param error If an error occurs, upon return contains an `NSError` object that describes the problem. - - @return `YES` if the file data was successfully appended otherwise `NO`. - */ -- (BOOL)appendPartWithFileURL:(NSURL *)fileURL - name:(NSString *)name - fileName:(NSString *)fileName - mimeType:(NSString *)mimeType - error:(NSError * _Nullable __autoreleasing *)error; - -/** - Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the data from the input stream and the multipart form boundary. - - @param inputStream The input stream to be appended to the form data - @param name The name to be associated with the specified input stream. This parameter must not be `nil`. - @param fileName The filename to be associated with the specified input stream. This parameter must not be `nil`. - @param length The length of the specified input stream in bytes. - @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. - */ -- (void)appendPartWithInputStream:(nullable NSInputStream *)inputStream - name:(NSString *)name - fileName:(NSString *)fileName - length:(int64_t)length - mimeType:(NSString *)mimeType; - -/** - Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. - - @param data The data to be encoded and appended to the form data. - @param name The name to be associated with the specified data. This parameter must not be `nil`. - @param fileName The filename to be associated with the specified data. This parameter must not be `nil`. - @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. - */ -- (void)appendPartWithFileData:(NSData *)data - name:(NSString *)name - fileName:(NSString *)fileName - mimeType:(NSString *)mimeType; - -/** - Appends the HTTP headers `Content-Disposition: form-data; name=#{name}"`, followed by the encoded data and the multipart form boundary. - - @param data The data to be encoded and appended to the form data. - @param name The name to be associated with the specified data. This parameter must not be `nil`. - */ - -- (void)appendPartWithFormData:(NSData *)data - name:(NSString *)name; - - -/** - Appends HTTP headers, followed by the encoded data and the multipart form boundary. - - @param headers The HTTP headers to be appended to the form data. - @param body The data to be encoded and appended to the form data. This parameter must not be `nil`. - */ -- (void)appendPartWithHeaders:(nullable NSDictionary *)headers - body:(NSData *)body; - -/** - Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream. - - When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth. - - @param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb. - @param delay Duration of delay each time a packet is read. By default, no delay is set. - */ -- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes - delay:(NSTimeInterval)delay; - -@end - -#pragma mark - - -/** - `AFJSONRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`. - */ -@interface AFJSONRequestSerializer : AFHTTPRequestSerializer - -/** - Options for writing the request JSON data from Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONWritingOptions". `0` by default. - */ -@property (nonatomic, assign) NSJSONWritingOptions writingOptions; - -/** - Creates and returns a JSON serializer with specified reading and writing options. - - @param writingOptions The specified JSON writing options. - */ -+ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions; - -@end - -#pragma mark - - -/** - `AFPropertyListRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`. - */ -@interface AFPropertyListRequestSerializer : AFHTTPRequestSerializer - -/** - The property list format. Possible values are described in "NSPropertyListFormat". - */ -@property (nonatomic, assign) NSPropertyListFormat format; - -/** - @warning The `writeOptions` property is currently unused. - */ -@property (nonatomic, assign) NSPropertyListWriteOptions writeOptions; - -/** - Creates and returns a property list serializer with a specified format, read options, and write options. - - @param format The property list format. - @param writeOptions The property list write options. - - @warning The `writeOptions` property is currently unused. - */ -+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format - writeOptions:(NSPropertyListWriteOptions)writeOptions; - -@end - -#pragma mark - - -///---------------- -/// @name Constants -///---------------- - -/** - ## Error Domains - - The following error domain is predefined. - - - `NSString * const AFURLRequestSerializationErrorDomain` - - ### Constants - - `AFURLRequestSerializationErrorDomain` - AFURLRequestSerializer errors. Error codes for `AFURLRequestSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. - */ -FOUNDATION_EXPORT NSString * const AFURLRequestSerializationErrorDomain; - -/** - ## User info dictionary keys - - These keys may exist in the user info dictionary, in addition to those defined for NSError. - - - `NSString * const AFNetworkingOperationFailingURLRequestErrorKey` - - ### Constants - - `AFNetworkingOperationFailingURLRequestErrorKey` - The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `AFURLRequestSerializationErrorDomain`. - */ -FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLRequestErrorKey; - -/** - ## Throttling Bandwidth for HTTP Request Input Streams - - @see -throttleBandwidthWithPacketSize:delay: - - ### Constants - - `kAFUploadStream3GSuggestedPacketSize` - Maximum packet size, in number of bytes. Equal to 16kb. - - `kAFUploadStream3GSuggestedDelay` - Duration of delay each time a packet is read. Equal to 0.2 seconds. - */ -FOUNDATION_EXPORT NSUInteger const kAFUploadStream3GSuggestedPacketSize; -FOUNDATION_EXPORT NSTimeInterval const kAFUploadStream3GSuggestedDelay; - -NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m deleted file mode 100644 index 9a2ac98..0000000 --- a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m +++ /dev/null @@ -1,1376 +0,0 @@ -// AFURLRequestSerialization.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "AFURLRequestSerialization.h" - -#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV -#import -#else -#import -#endif - -NSString * const AFURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request"; -NSString * const AFNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response"; - -typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error); - -/** - Returns a percent-escaped string following RFC 3986 for a query string key or value. - RFC 3986 states that the following characters are "reserved" characters. - - General Delimiters: ":", "#", "[", "]", "@", "?", "/" - - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" - - In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow - query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" - should be percent-escaped in the query string. - - parameter string: The string to be percent-escaped. - - returns: The percent-escaped string. - */ -NSString * AFPercentEscapedStringFromString(NSString *string) { - static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4 - static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;="; - - NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; - [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]]; - - // FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028 - // return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; - - static NSUInteger const batchSize = 50; - - NSUInteger index = 0; - NSMutableString *escaped = @"".mutableCopy; - - while (index < string.length) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wgnu" - NSUInteger length = MIN(string.length - index, batchSize); -#pragma GCC diagnostic pop - NSRange range = NSMakeRange(index, length); - - // To avoid breaking up character sequences such as 👴🏻👮🏽 - range = [string rangeOfComposedCharacterSequencesForRange:range]; - - NSString *substring = [string substringWithRange:range]; - NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; - [escaped appendString:encoded]; - - index += range.length; - } - - return escaped; -} - -#pragma mark - - -@interface AFQueryStringPair : NSObject -@property (readwrite, nonatomic, strong) id field; -@property (readwrite, nonatomic, strong) id value; - -- (instancetype)initWithField:(id)field value:(id)value; - -- (NSString *)URLEncodedStringValue; -@end - -@implementation AFQueryStringPair - -- (instancetype)initWithField:(id)field value:(id)value { - self = [super init]; - if (!self) { - return nil; - } - - self.field = field; - self.value = value; - - return self; -} - -- (NSString *)URLEncodedStringValue { - if (!self.value || [self.value isEqual:[NSNull null]]) { - return AFPercentEscapedStringFromString([self.field description]); - } else { - return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])]; - } -} - -@end - -#pragma mark - - -FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary); -FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value); - -NSString * AFQueryStringFromParameters(NSDictionary *parameters) { - NSMutableArray *mutablePairs = [NSMutableArray array]; - for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) { - [mutablePairs addObject:[pair URLEncodedStringValue]]; - } - - return [mutablePairs componentsJoinedByString:@"&"]; -} - -NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) { - return AFQueryStringPairsFromKeyAndValue(nil, dictionary); -} - -NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) { - NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; - - NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)]; - - if ([value isKindOfClass:[NSDictionary class]]) { - NSDictionary *dictionary = value; - // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries - for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { - id nestedValue = dictionary[nestedKey]; - if (nestedValue) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)]; - } - } - } else if ([value isKindOfClass:[NSArray class]]) { - NSArray *array = value; - for (id nestedValue in array) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; - } - } else if ([value isKindOfClass:[NSSet class]]) { - NSSet *set = value; - for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)]; - } - } else { - [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]]; - } - - return mutableQueryStringComponents; -} - -#pragma mark - - -@interface AFStreamingMultipartFormData : NSObject -- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest - stringEncoding:(NSStringEncoding)encoding; - -- (NSMutableURLRequest *)requestByFinalizingMultipartFormData; -@end - -#pragma mark - - -static NSArray * AFHTTPRequestSerializerObservedKeyPaths() { - static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))]; - }); - - return _AFHTTPRequestSerializerObservedKeyPaths; -} - -static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext; - -@interface AFHTTPRequestSerializer () -@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths; -@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders; -@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle; -@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization; -@end - -@implementation AFHTTPRequestSerializer - -+ (instancetype)serializer { - return [[self alloc] init]; -} - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - - self.stringEncoding = NSUTF8StringEncoding; - - self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; - - // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 - NSMutableArray *acceptLanguagesComponents = [NSMutableArray array]; - [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - float q = 1.0f - (idx * 0.1f); - [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]]; - *stop = q <= 0.5f; - }]; - [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"]; - - NSString *userAgent = nil; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" -#if TARGET_OS_IOS - // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 - userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]]; -#elif TARGET_OS_WATCH - // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 - userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]]; -#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) - userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]]; -#endif -#pragma clang diagnostic pop - if (userAgent) { - if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) { - NSMutableString *mutableUserAgent = [userAgent mutableCopy]; - if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) { - userAgent = mutableUserAgent; - } - } - [self setValue:userAgent forHTTPHeaderField:@"User-Agent"]; - } - - // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html - self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil]; - - self.mutableObservedChangedKeyPaths = [NSMutableSet set]; - for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { - if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { - [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext]; - } - } - - return self; -} - -- (void)dealloc { - for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { - if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { - [self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext]; - } - } -} - -#pragma mark - - -// Workarounds for crashing behavior using Key-Value Observing with XCTest -// See https://github.com/AFNetworking/AFNetworking/issues/2523 - -- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess { - [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))]; - _allowsCellularAccess = allowsCellularAccess; - [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))]; -} - -- (void)setCachePolicy:(NSURLRequestCachePolicy)cachePolicy { - [self willChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))]; - _cachePolicy = cachePolicy; - [self didChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))]; -} - -- (void)setHTTPShouldHandleCookies:(BOOL)HTTPShouldHandleCookies { - [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; - _HTTPShouldHandleCookies = HTTPShouldHandleCookies; - [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; -} - -- (void)setHTTPShouldUsePipelining:(BOOL)HTTPShouldUsePipelining { - [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))]; - _HTTPShouldUsePipelining = HTTPShouldUsePipelining; - [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))]; -} - -- (void)setNetworkServiceType:(NSURLRequestNetworkServiceType)networkServiceType { - [self willChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))]; - _networkServiceType = networkServiceType; - [self didChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))]; -} - -- (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval { - [self willChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))]; - _timeoutInterval = timeoutInterval; - [self didChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))]; -} - -#pragma mark - - -- (NSDictionary *)HTTPRequestHeaders { - return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; -} - -- (void)setValue:(NSString *)value -forHTTPHeaderField:(NSString *)field -{ - [self.mutableHTTPRequestHeaders setValue:value forKey:field]; -} - -- (NSString *)valueForHTTPHeaderField:(NSString *)field { - return [self.mutableHTTPRequestHeaders valueForKey:field]; -} - -- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username - password:(NSString *)password -{ - NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding]; - NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]; - [self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"]; -} - -- (void)clearAuthorizationHeader { - [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"]; -} - -#pragma mark - - -- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style { - self.queryStringSerializationStyle = style; - self.queryStringSerialization = nil; -} - -- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, NSError *__autoreleasing *))block { - self.queryStringSerialization = block; -} - -#pragma mark - - -- (NSMutableURLRequest *)requestWithMethod:(NSString *)method - URLString:(NSString *)URLString - parameters:(id)parameters - error:(NSError *__autoreleasing *)error -{ - NSParameterAssert(method); - NSParameterAssert(URLString); - - NSURL *url = [NSURL URLWithString:URLString]; - - NSParameterAssert(url); - - NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url]; - mutableRequest.HTTPMethod = method; - - for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { - if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) { - [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath]; - } - } - - mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy]; - - return mutableRequest; -} - -- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method - URLString:(NSString *)URLString - parameters:(NSDictionary *)parameters - constructingBodyWithBlock:(void (^)(id formData))block - error:(NSError *__autoreleasing *)error -{ - NSParameterAssert(method); - NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]); - - NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error]; - - __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding]; - - if (parameters) { - for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) { - NSData *data = nil; - if ([pair.value isKindOfClass:[NSData class]]) { - data = pair.value; - } else if ([pair.value isEqual:[NSNull null]]) { - data = [NSData data]; - } else { - data = [[pair.value description] dataUsingEncoding:self.stringEncoding]; - } - - if (data) { - [formData appendPartWithFormData:data name:[pair.field description]]; - } - } - } - - if (block) { - block(formData); - } - - return [formData requestByFinalizingMultipartFormData]; -} - -- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request - writingStreamContentsToFile:(NSURL *)fileURL - completionHandler:(void (^)(NSError *error))handler -{ - NSParameterAssert(request.HTTPBodyStream); - NSParameterAssert([fileURL isFileURL]); - - NSInputStream *inputStream = request.HTTPBodyStream; - NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO]; - __block NSError *error = nil; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - - [inputStream open]; - [outputStream open]; - - while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) { - uint8_t buffer[1024]; - - NSInteger bytesRead = [inputStream read:buffer maxLength:1024]; - if (inputStream.streamError || bytesRead < 0) { - error = inputStream.streamError; - break; - } - - NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead]; - if (outputStream.streamError || bytesWritten < 0) { - error = outputStream.streamError; - break; - } - - if (bytesRead == 0 && bytesWritten == 0) { - break; - } - } - - [outputStream close]; - [inputStream close]; - - if (handler) { - dispatch_async(dispatch_get_main_queue(), ^{ - handler(error); - }); - } - }); - - NSMutableURLRequest *mutableRequest = [request mutableCopy]; - mutableRequest.HTTPBodyStream = nil; - - return mutableRequest; -} - -#pragma mark - AFURLRequestSerialization - -- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request - withParameters:(id)parameters - error:(NSError *__autoreleasing *)error -{ - NSParameterAssert(request); - - NSMutableURLRequest *mutableRequest = [request mutableCopy]; - - [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { - if (![request valueForHTTPHeaderField:field]) { - [mutableRequest setValue:value forHTTPHeaderField:field]; - } - }]; - - NSString *query = nil; - if (parameters) { - if (self.queryStringSerialization) { - NSError *serializationError; - query = self.queryStringSerialization(request, parameters, &serializationError); - - if (serializationError) { - if (error) { - *error = serializationError; - } - - return nil; - } - } else { - switch (self.queryStringSerializationStyle) { - case AFHTTPRequestQueryStringDefaultStyle: - query = AFQueryStringFromParameters(parameters); - break; - } - } - } - - if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { - if (query && query.length > 0) { - mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]]; - } - } else { - // #2864: an empty string is a valid x-www-form-urlencoded payload - if (!query) { - query = @""; - } - if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { - [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; - } - [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; - } - - return mutableRequest; -} - -#pragma mark - NSKeyValueObserving - -+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { - if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) { - return NO; - } - - return [super automaticallyNotifiesObserversForKey:key]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(__unused id)object - change:(NSDictionary *)change - context:(void *)context -{ - if (context == AFHTTPRequestSerializerObserverContext) { - if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) { - [self.mutableObservedChangedKeyPaths removeObject:keyPath]; - } else { - [self.mutableObservedChangedKeyPaths addObject:keyPath]; - } - } -} - -#pragma mark - NSSecureCoding - -+ (BOOL)supportsSecureCoding { - return YES; -} - -- (instancetype)initWithCoder:(NSCoder *)decoder { - self = [self init]; - if (!self) { - return nil; - } - - self.mutableHTTPRequestHeaders = [[decoder decodeObjectOfClass:[NSDictionary class] forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))] mutableCopy]; - self.queryStringSerializationStyle = (AFHTTPRequestQueryStringSerializationStyle)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))] unsignedIntegerValue]; - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))]; - [coder encodeInteger:self.queryStringSerializationStyle forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init]; - serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone]; - serializer.queryStringSerializationStyle = self.queryStringSerializationStyle; - serializer.queryStringSerialization = self.queryStringSerialization; - - return serializer; -} - -@end - -#pragma mark - - -static NSString * AFCreateMultipartFormBoundary() { - return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()]; -} - -static NSString * const kAFMultipartFormCRLF = @"\r\n"; - -static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) { - return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF]; -} - -static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) { - return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; -} - -static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) { - return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; -} - -static inline NSString * AFContentTypeForPathExtension(NSString *extension) { - NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); - NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType); - if (!contentType) { - return @"application/octet-stream"; - } else { - return contentType; - } -} - -NSUInteger const kAFUploadStream3GSuggestedPacketSize = 1024 * 16; -NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2; - -@interface AFHTTPBodyPart : NSObject -@property (nonatomic, assign) NSStringEncoding stringEncoding; -@property (nonatomic, strong) NSDictionary *headers; -@property (nonatomic, copy) NSString *boundary; -@property (nonatomic, strong) id body; -@property (nonatomic, assign) unsigned long long bodyContentLength; -@property (nonatomic, strong) NSInputStream *inputStream; - -@property (nonatomic, assign) BOOL hasInitialBoundary; -@property (nonatomic, assign) BOOL hasFinalBoundary; - -@property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable; -@property (readonly, nonatomic, assign) unsigned long long contentLength; - -- (NSInteger)read:(uint8_t *)buffer - maxLength:(NSUInteger)length; -@end - -@interface AFMultipartBodyStream : NSInputStream -@property (nonatomic, assign) NSUInteger numberOfBytesInPacket; -@property (nonatomic, assign) NSTimeInterval delay; -@property (nonatomic, strong) NSInputStream *inputStream; -@property (readonly, nonatomic, assign) unsigned long long contentLength; -@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty; - -- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding; -- (void)setInitialAndFinalBoundaries; -- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart; -@end - -#pragma mark - - -@interface AFStreamingMultipartFormData () -@property (readwrite, nonatomic, copy) NSMutableURLRequest *request; -@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; -@property (readwrite, nonatomic, copy) NSString *boundary; -@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream; -@end - -@implementation AFStreamingMultipartFormData - -- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest - stringEncoding:(NSStringEncoding)encoding -{ - self = [super init]; - if (!self) { - return nil; - } - - self.request = urlRequest; - self.stringEncoding = encoding; - self.boundary = AFCreateMultipartFormBoundary(); - self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding]; - - return self; -} - -- (BOOL)appendPartWithFileURL:(NSURL *)fileURL - name:(NSString *)name - error:(NSError * __autoreleasing *)error -{ - NSParameterAssert(fileURL); - NSParameterAssert(name); - - NSString *fileName = [fileURL lastPathComponent]; - NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]); - - return [self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error]; -} - -- (BOOL)appendPartWithFileURL:(NSURL *)fileURL - name:(NSString *)name - fileName:(NSString *)fileName - mimeType:(NSString *)mimeType - error:(NSError * __autoreleasing *)error -{ - NSParameterAssert(fileURL); - NSParameterAssert(name); - NSParameterAssert(fileName); - NSParameterAssert(mimeType); - - if (![fileURL isFileURL]) { - NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)}; - if (error) { - *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; - } - - return NO; - } else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) { - NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)}; - if (error) { - *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; - } - - return NO; - } - - NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error]; - if (!fileAttributes) { - return NO; - } - - NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; - [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; - [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; - - AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; - bodyPart.stringEncoding = self.stringEncoding; - bodyPart.headers = mutableHeaders; - bodyPart.boundary = self.boundary; - bodyPart.body = fileURL; - bodyPart.bodyContentLength = [fileAttributes[NSFileSize] unsignedLongLongValue]; - [self.bodyStream appendHTTPBodyPart:bodyPart]; - - return YES; -} - -- (void)appendPartWithInputStream:(NSInputStream *)inputStream - name:(NSString *)name - fileName:(NSString *)fileName - length:(int64_t)length - mimeType:(NSString *)mimeType -{ - NSParameterAssert(name); - NSParameterAssert(fileName); - NSParameterAssert(mimeType); - - NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; - [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; - [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; - - AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; - bodyPart.stringEncoding = self.stringEncoding; - bodyPart.headers = mutableHeaders; - bodyPart.boundary = self.boundary; - bodyPart.body = inputStream; - - bodyPart.bodyContentLength = (unsigned long long)length; - - [self.bodyStream appendHTTPBodyPart:bodyPart]; -} - -- (void)appendPartWithFileData:(NSData *)data - name:(NSString *)name - fileName:(NSString *)fileName - mimeType:(NSString *)mimeType -{ - NSParameterAssert(name); - NSParameterAssert(fileName); - NSParameterAssert(mimeType); - - NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; - [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; - [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; - - [self appendPartWithHeaders:mutableHeaders body:data]; -} - -- (void)appendPartWithFormData:(NSData *)data - name:(NSString *)name -{ - NSParameterAssert(name); - - NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; - [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"]; - - [self appendPartWithHeaders:mutableHeaders body:data]; -} - -- (void)appendPartWithHeaders:(NSDictionary *)headers - body:(NSData *)body -{ - NSParameterAssert(body); - - AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; - bodyPart.stringEncoding = self.stringEncoding; - bodyPart.headers = headers; - bodyPart.boundary = self.boundary; - bodyPart.bodyContentLength = [body length]; - bodyPart.body = body; - - [self.bodyStream appendHTTPBodyPart:bodyPart]; -} - -- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes - delay:(NSTimeInterval)delay -{ - self.bodyStream.numberOfBytesInPacket = numberOfBytes; - self.bodyStream.delay = delay; -} - -- (NSMutableURLRequest *)requestByFinalizingMultipartFormData { - if ([self.bodyStream isEmpty]) { - return self.request; - } - - // Reset the initial and final boundaries to ensure correct Content-Length - [self.bodyStream setInitialAndFinalBoundaries]; - [self.request setHTTPBodyStream:self.bodyStream]; - - [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"]; - [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"]; - - return self.request; -} - -@end - -#pragma mark - - -@interface NSStream () -@property (readwrite) NSStreamStatus streamStatus; -@property (readwrite, copy) NSError *streamError; -@end - -@interface AFMultipartBodyStream () -@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; -@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts; -@property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator; -@property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart; -@property (readwrite, nonatomic, strong) NSOutputStream *outputStream; -@property (readwrite, nonatomic, strong) NSMutableData *buffer; -@end - -@implementation AFMultipartBodyStream -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wimplicit-atomic-properties" -#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100) -@synthesize delegate; -#endif -@synthesize streamStatus; -@synthesize streamError; -#pragma clang diagnostic pop - -- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding { - self = [super init]; - if (!self) { - return nil; - } - - self.stringEncoding = encoding; - self.HTTPBodyParts = [NSMutableArray array]; - self.numberOfBytesInPacket = NSIntegerMax; - - return self; -} - -- (void)setInitialAndFinalBoundaries { - if ([self.HTTPBodyParts count] > 0) { - for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { - bodyPart.hasInitialBoundary = NO; - bodyPart.hasFinalBoundary = NO; - } - - [[self.HTTPBodyParts firstObject] setHasInitialBoundary:YES]; - [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES]; - } -} - -- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart { - [self.HTTPBodyParts addObject:bodyPart]; -} - -- (BOOL)isEmpty { - return [self.HTTPBodyParts count] == 0; -} - -#pragma mark - NSInputStream - -- (NSInteger)read:(uint8_t *)buffer - maxLength:(NSUInteger)length -{ - if ([self streamStatus] == NSStreamStatusClosed) { - return 0; - } - - NSInteger totalNumberOfBytesRead = 0; - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" - while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) { - if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) { - if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) { - break; - } - } else { - NSUInteger maxLength = MIN(length, self.numberOfBytesInPacket) - (NSUInteger)totalNumberOfBytesRead; - NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength]; - if (numberOfBytesRead == -1) { - self.streamError = self.currentHTTPBodyPart.inputStream.streamError; - break; - } else { - totalNumberOfBytesRead += numberOfBytesRead; - - if (self.delay > 0.0f) { - [NSThread sleepForTimeInterval:self.delay]; - } - } - } - } -#pragma clang diagnostic pop - - return totalNumberOfBytesRead; -} - -- (BOOL)getBuffer:(__unused uint8_t **)buffer - length:(__unused NSUInteger *)len -{ - return NO; -} - -- (BOOL)hasBytesAvailable { - return [self streamStatus] == NSStreamStatusOpen; -} - -#pragma mark - NSStream - -- (void)open { - if (self.streamStatus == NSStreamStatusOpen) { - return; - } - - self.streamStatus = NSStreamStatusOpen; - - [self setInitialAndFinalBoundaries]; - self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator]; -} - -- (void)close { - self.streamStatus = NSStreamStatusClosed; -} - -- (id)propertyForKey:(__unused NSString *)key { - return nil; -} - -- (BOOL)setProperty:(__unused id)property - forKey:(__unused NSString *)key -{ - return NO; -} - -- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop - forMode:(__unused NSString *)mode -{} - -- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop - forMode:(__unused NSString *)mode -{} - -- (unsigned long long)contentLength { - unsigned long long length = 0; - for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { - length += [bodyPart contentLength]; - } - - return length; -} - -#pragma mark - Undocumented CFReadStream Bridged Methods - -- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop - forMode:(__unused CFStringRef)aMode -{} - -- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop - forMode:(__unused CFStringRef)aMode -{} - -- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags - callback:(__unused CFReadStreamClientCallBack)inCallback - context:(__unused CFStreamClientContext *)inContext { - return NO; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding]; - - for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { - [bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]]; - } - - [bodyStreamCopy setInitialAndFinalBoundaries]; - - return bodyStreamCopy; -} - -@end - -#pragma mark - - -typedef enum { - AFEncapsulationBoundaryPhase = 1, - AFHeaderPhase = 2, - AFBodyPhase = 3, - AFFinalBoundaryPhase = 4, -} AFHTTPBodyPartReadPhase; - -@interface AFHTTPBodyPart () { - AFHTTPBodyPartReadPhase _phase; - NSInputStream *_inputStream; - unsigned long long _phaseReadOffset; -} - -- (BOOL)transitionToNextPhase; -- (NSInteger)readData:(NSData *)data - intoBuffer:(uint8_t *)buffer - maxLength:(NSUInteger)length; -@end - -@implementation AFHTTPBodyPart - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - - [self transitionToNextPhase]; - - return self; -} - -- (void)dealloc { - if (_inputStream) { - [_inputStream close]; - _inputStream = nil; - } -} - -- (NSInputStream *)inputStream { - if (!_inputStream) { - if ([self.body isKindOfClass:[NSData class]]) { - _inputStream = [NSInputStream inputStreamWithData:self.body]; - } else if ([self.body isKindOfClass:[NSURL class]]) { - _inputStream = [NSInputStream inputStreamWithURL:self.body]; - } else if ([self.body isKindOfClass:[NSInputStream class]]) { - _inputStream = self.body; - } else { - _inputStream = [NSInputStream inputStreamWithData:[NSData data]]; - } - } - - return _inputStream; -} - -- (NSString *)stringForHeaders { - NSMutableString *headerString = [NSMutableString string]; - for (NSString *field in [self.headers allKeys]) { - [headerString appendString:[NSString stringWithFormat:@"%@: %@%@", field, [self.headers valueForKey:field], kAFMultipartFormCRLF]]; - } - [headerString appendString:kAFMultipartFormCRLF]; - - return [NSString stringWithString:headerString]; -} - -- (unsigned long long)contentLength { - unsigned long long length = 0; - - NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; - length += [encapsulationBoundaryData length]; - - NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; - length += [headersData length]; - - length += _bodyContentLength; - - NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); - length += [closingBoundaryData length]; - - return length; -} - -- (BOOL)hasBytesAvailable { - // Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the available buffer - if (_phase == AFFinalBoundaryPhase) { - return YES; - } - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcovered-switch-default" - switch (self.inputStream.streamStatus) { - case NSStreamStatusNotOpen: - case NSStreamStatusOpening: - case NSStreamStatusOpen: - case NSStreamStatusReading: - case NSStreamStatusWriting: - return YES; - case NSStreamStatusAtEnd: - case NSStreamStatusClosed: - case NSStreamStatusError: - default: - return NO; - } -#pragma clang diagnostic pop -} - -- (NSInteger)read:(uint8_t *)buffer - maxLength:(NSUInteger)length -{ - NSInteger totalNumberOfBytesRead = 0; - - if (_phase == AFEncapsulationBoundaryPhase) { - NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; - totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; - } - - if (_phase == AFHeaderPhase) { - NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; - totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; - } - - if (_phase == AFBodyPhase) { - NSInteger numberOfBytesRead = 0; - - numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; - if (numberOfBytesRead == -1) { - return -1; - } else { - totalNumberOfBytesRead += numberOfBytesRead; - - if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) { - [self transitionToNextPhase]; - } - } - } - - if (_phase == AFFinalBoundaryPhase) { - NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); - totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; - } - - return totalNumberOfBytesRead; -} - -- (NSInteger)readData:(NSData *)data - intoBuffer:(uint8_t *)buffer - maxLength:(NSUInteger)length -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" - NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length)); - [data getBytes:buffer range:range]; -#pragma clang diagnostic pop - - _phaseReadOffset += range.length; - - if (((NSUInteger)_phaseReadOffset) >= [data length]) { - [self transitionToNextPhase]; - } - - return (NSInteger)range.length; -} - -- (BOOL)transitionToNextPhase { - if (![[NSThread currentThread] isMainThread]) { - dispatch_sync(dispatch_get_main_queue(), ^{ - [self transitionToNextPhase]; - }); - return YES; - } - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcovered-switch-default" - switch (_phase) { - case AFEncapsulationBoundaryPhase: - _phase = AFHeaderPhase; - break; - case AFHeaderPhase: - [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; - [self.inputStream open]; - _phase = AFBodyPhase; - break; - case AFBodyPhase: - [self.inputStream close]; - _phase = AFFinalBoundaryPhase; - break; - case AFFinalBoundaryPhase: - default: - _phase = AFEncapsulationBoundaryPhase; - break; - } - _phaseReadOffset = 0; -#pragma clang diagnostic pop - - return YES; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init]; - - bodyPart.stringEncoding = self.stringEncoding; - bodyPart.headers = self.headers; - bodyPart.bodyContentLength = self.bodyContentLength; - bodyPart.body = self.body; - bodyPart.boundary = self.boundary; - - return bodyPart; -} - -@end - -#pragma mark - - -@implementation AFJSONRequestSerializer - -+ (instancetype)serializer { - return [self serializerWithWritingOptions:(NSJSONWritingOptions)0]; -} - -+ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions -{ - AFJSONRequestSerializer *serializer = [[self alloc] init]; - serializer.writingOptions = writingOptions; - - return serializer; -} - -#pragma mark - AFURLRequestSerialization - -- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request - withParameters:(id)parameters - error:(NSError *__autoreleasing *)error -{ - NSParameterAssert(request); - - if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { - return [super requestBySerializingRequest:request withParameters:parameters error:error]; - } - - NSMutableURLRequest *mutableRequest = [request mutableCopy]; - - [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { - if (![request valueForHTTPHeaderField:field]) { - [mutableRequest setValue:value forHTTPHeaderField:field]; - } - }]; - - if (parameters) { - if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { - [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; - } - - [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]]; - } - - return mutableRequest; -} - -#pragma mark - NSSecureCoding - -- (instancetype)initWithCoder:(NSCoder *)decoder { - self = [super initWithCoder:decoder]; - if (!self) { - return nil; - } - - self.writingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writingOptions))] unsignedIntegerValue]; - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [super encodeWithCoder:coder]; - - [coder encodeInteger:self.writingOptions forKey:NSStringFromSelector(@selector(writingOptions))]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFJSONRequestSerializer *serializer = [super copyWithZone:zone]; - serializer.writingOptions = self.writingOptions; - - return serializer; -} - -@end - -#pragma mark - - -@implementation AFPropertyListRequestSerializer - -+ (instancetype)serializer { - return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 writeOptions:0]; -} - -+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format - writeOptions:(NSPropertyListWriteOptions)writeOptions -{ - AFPropertyListRequestSerializer *serializer = [[self alloc] init]; - serializer.format = format; - serializer.writeOptions = writeOptions; - - return serializer; -} - -#pragma mark - AFURLRequestSerializer - -- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request - withParameters:(id)parameters - error:(NSError *__autoreleasing *)error -{ - NSParameterAssert(request); - - if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { - return [super requestBySerializingRequest:request withParameters:parameters error:error]; - } - - NSMutableURLRequest *mutableRequest = [request mutableCopy]; - - [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { - if (![request valueForHTTPHeaderField:field]) { - [mutableRequest setValue:value forHTTPHeaderField:field]; - } - }]; - - if (parameters) { - if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { - [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"]; - } - - [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]]; - } - - return mutableRequest; -} - -#pragma mark - NSSecureCoding - -- (instancetype)initWithCoder:(NSCoder *)decoder { - self = [super initWithCoder:decoder]; - if (!self) { - return nil; - } - - self.format = (NSPropertyListFormat)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue]; - self.writeOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writeOptions))] unsignedIntegerValue]; - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [super encodeWithCoder:coder]; - - [coder encodeInteger:self.format forKey:NSStringFromSelector(@selector(format))]; - [coder encodeObject:@(self.writeOptions) forKey:NSStringFromSelector(@selector(writeOptions))]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFPropertyListRequestSerializer *serializer = [super copyWithZone:zone]; - serializer.format = self.format; - serializer.writeOptions = self.writeOptions; - - return serializer; -} - -@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h deleted file mode 100644 index a9430ad..0000000 --- a/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h +++ /dev/null @@ -1,311 +0,0 @@ -// AFURLResponseSerialization.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data. - - For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object. - */ -@protocol AFURLResponseSerialization - -/** - The response object decoded from the data associated with a specified response. - - @param response The response to be processed. - @param data The response data to be decoded. - @param error The error that occurred while attempting to decode the response data. - - @return The object decoded from the specified response data. - */ -- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response - data:(nullable NSData *)data - error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW; - -@end - -#pragma mark - - -/** - `AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. - - Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior. - */ -@interface AFHTTPResponseSerializer : NSObject - -- (instancetype)init; - -/** - The string encoding used to serialize data received from the server, when no string encoding is specified by the response. `NSUTF8StringEncoding` by default. - */ -@property (nonatomic, assign) NSStringEncoding stringEncoding; - -/** - Creates and returns a serializer with default configuration. - */ -+ (instancetype)serializer; - -///----------------------------------------- -/// @name Configuring Response Serialization -///----------------------------------------- - -/** - The acceptable HTTP status codes for responses. When non-`nil`, responses with status codes not contained by the set will result in an error during validation. - - See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html - */ -@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes; - -/** - The acceptable MIME types for responses. When non-`nil`, responses with a `Content-Type` with MIME types that do not intersect with the set will result in an error during validation. - */ -@property (nonatomic, copy, nullable) NSSet *acceptableContentTypes; - -/** - Validates the specified response and data. - - In its base implementation, this method checks for an acceptable status code and content type. Subclasses may wish to add other domain-specific checks. - - @param response The response to be validated. - @param data The data associated with the response. - @param error The error that occurred while attempting to validate the response. - - @return `YES` if the response is valid, otherwise `NO`. - */ -- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response - data:(nullable NSData *)data - error:(NSError * _Nullable __autoreleasing *)error; - -@end - -#pragma mark - - - -/** - `AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses. - - By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types: - - - `application/json` - - `text/json` - - `text/javascript` - */ -@interface AFJSONResponseSerializer : AFHTTPResponseSerializer - -- (instancetype)init; - -/** - Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. - */ -@property (nonatomic, assign) NSJSONReadingOptions readingOptions; - -/** - Whether to remove keys with `NSNull` values from response JSON. Defaults to `NO`. - */ -@property (nonatomic, assign) BOOL removesKeysWithNullValues; - -/** - Creates and returns a JSON serializer with specified reading and writing options. - - @param readingOptions The specified JSON reading options. - */ -+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions; - -@end - -#pragma mark - - -/** - `AFXMLParserResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. - - By default, `AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: - - - `application/xml` - - `text/xml` - */ -@interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer - -@end - -#pragma mark - - -#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED - -/** - `AFXMLDocumentResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. - - By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: - - - `application/xml` - - `text/xml` - */ -@interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer - -- (instancetype)init; - -/** - Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. - */ -@property (nonatomic, assign) NSUInteger options; - -/** - Creates and returns an XML document serializer with the specified options. - - @param mask The XML document options. - */ -+ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask; - -@end - -#endif - -#pragma mark - - -/** - `AFPropertyListResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. - - By default, `AFPropertyListResponseSerializer` accepts the following MIME types: - - - `application/x-plist` - */ -@interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer - -- (instancetype)init; - -/** - The property list format. Possible values are described in "NSPropertyListFormat". - */ -@property (nonatomic, assign) NSPropertyListFormat format; - -/** - The property list reading options. Possible values are described in "NSPropertyListMutabilityOptions." - */ -@property (nonatomic, assign) NSPropertyListReadOptions readOptions; - -/** - Creates and returns a property list serializer with a specified format, read options, and write options. - - @param format The property list format. - @param readOptions The property list reading options. - */ -+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format - readOptions:(NSPropertyListReadOptions)readOptions; - -@end - -#pragma mark - - -/** - `AFImageResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses. - - By default, `AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: - - - `image/tiff` - - `image/jpeg` - - `image/gif` - - `image/png` - - `image/ico` - - `image/x-icon` - - `image/bmp` - - `image/x-bmp` - - `image/x-xbitmap` - - `image/x-win-bitmap` - */ -@interface AFImageResponseSerializer : AFHTTPResponseSerializer - -#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH -/** - The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of scale of the main screen by default, which automatically scales images for retina displays, for instance. - */ -@property (nonatomic, assign) CGFloat imageScale; - -/** - Whether to automatically inflate response image data for compressed formats (such as PNG or JPEG). Enabling this can significantly improve drawing performance on iOS when used with `setCompletionBlockWithSuccess:failure:`, as it allows a bitmap representation to be constructed in the background rather than on the main thread. `YES` by default. - */ -@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage; -#endif - -@end - -#pragma mark - - -/** - `AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer. - */ -@interface AFCompoundResponseSerializer : AFHTTPResponseSerializer - -/** - The component response serializers. - */ -@property (readonly, nonatomic, copy) NSArray > *responseSerializers; - -/** - Creates and returns a compound serializer comprised of the specified response serializers. - - @warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`. - */ -+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray > *)responseSerializers; - -@end - -///---------------- -/// @name Constants -///---------------- - -/** - ## Error Domains - - The following error domain is predefined. - - - `NSString * const AFURLResponseSerializationErrorDomain` - - ### Constants - - `AFURLResponseSerializationErrorDomain` - AFURLResponseSerializer errors. Error codes for `AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. - */ -FOUNDATION_EXPORT NSString * const AFURLResponseSerializationErrorDomain; - -/** - ## User info dictionary keys - - These keys may exist in the user info dictionary, in addition to those defined for NSError. - - - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey` - - `NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey` - - ### Constants - - `AFNetworkingOperationFailingURLResponseErrorKey` - The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. - - `AFNetworkingOperationFailingURLResponseDataErrorKey` - The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. - */ -FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseErrorKey; - -FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey; - -NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m deleted file mode 100755 index 17372b9..0000000 --- a/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m +++ /dev/null @@ -1,805 +0,0 @@ -// AFURLResponseSerialization.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "AFURLResponseSerialization.h" - -#import - -#if TARGET_OS_IOS -#import -#elif TARGET_OS_WATCH -#import -#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -#import -#endif - -NSString * const AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response"; -NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response"; -NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data"; - -static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) { - if (!error) { - return underlyingError; - } - - if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) { - return error; - } - - NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy]; - mutableUserInfo[NSUnderlyingErrorKey] = underlyingError; - - return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo]; -} - -static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) { - if ([error.domain isEqualToString:domain] && error.code == code) { - return YES; - } else if (error.userInfo[NSUnderlyingErrorKey]) { - return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain); - } - - return NO; -} - -static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) { - if ([JSONObject isKindOfClass:[NSArray class]]) { - NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]]; - for (id value in (NSArray *)JSONObject) { - [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)]; - } - - return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray]; - } else if ([JSONObject isKindOfClass:[NSDictionary class]]) { - NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject]; - for (id key in [(NSDictionary *)JSONObject allKeys]) { - id value = (NSDictionary *)JSONObject[key]; - if (!value || [value isEqual:[NSNull null]]) { - [mutableDictionary removeObjectForKey:key]; - } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) { - mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions); - } - } - - return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary]; - } - - return JSONObject; -} - -@implementation AFHTTPResponseSerializer - -+ (instancetype)serializer { - return [[self alloc] init]; -} - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - - self.stringEncoding = NSUTF8StringEncoding; - - self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; - self.acceptableContentTypes = nil; - - return self; -} - -#pragma mark - - -- (BOOL)validateResponse:(NSHTTPURLResponse *)response - data:(NSData *)data - error:(NSError * __autoreleasing *)error -{ - BOOL responseIsValid = YES; - NSError *validationError = nil; - - if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) { - if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] && - !([response MIMEType] == nil && [data length] == 0)) { - - if ([data length] > 0 && [response URL]) { - NSMutableDictionary *mutableUserInfo = [@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]], - NSURLErrorFailingURLErrorKey:[response URL], - AFNetworkingOperationFailingURLResponseErrorKey: response, - } mutableCopy]; - if (data) { - mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; - } - - validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError); - } - - responseIsValid = NO; - } - - if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) { - NSMutableDictionary *mutableUserInfo = [@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode], - NSURLErrorFailingURLErrorKey:[response URL], - AFNetworkingOperationFailingURLResponseErrorKey: response, - } mutableCopy]; - - if (data) { - mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; - } - - validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError); - - responseIsValid = NO; - } - } - - if (error && !responseIsValid) { - *error = validationError; - } - - return responseIsValid; -} - -#pragma mark - AFURLResponseSerialization - -- (id)responseObjectForResponse:(NSURLResponse *)response - data:(NSData *)data - error:(NSError *__autoreleasing *)error -{ - [self validateResponse:(NSHTTPURLResponse *)response data:data error:error]; - - return data; -} - -#pragma mark - NSSecureCoding - -+ (BOOL)supportsSecureCoding { - return YES; -} - -- (instancetype)initWithCoder:(NSCoder *)decoder { - self = [self init]; - if (!self) { - return nil; - } - - self.acceptableStatusCodes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; - self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:self.acceptableStatusCodes forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; - [coder encodeObject:self.acceptableContentTypes forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; - serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone]; - serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone]; - - return serializer; -} - -@end - -#pragma mark - - -@implementation AFJSONResponseSerializer - -+ (instancetype)serializer { - return [self serializerWithReadingOptions:(NSJSONReadingOptions)0]; -} - -+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions { - AFJSONResponseSerializer *serializer = [[self alloc] init]; - serializer.readingOptions = readingOptions; - - return serializer; -} - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - - self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil]; - - return self; -} - -#pragma mark - AFURLResponseSerialization - -- (id)responseObjectForResponse:(NSURLResponse *)response - data:(NSData *)data - error:(NSError *__autoreleasing *)error -{ - if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { - if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { - return nil; - } - } - - id responseObject = nil; - NSError *serializationError = nil; - // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization. - // See https://github.com/rails/rails/issues/1742 - BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]]; - if (data.length > 0 && !isSpace) { - responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError]; - } else { - return nil; - } - - if (self.removesKeysWithNullValues && responseObject) { - responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); - } - - if (error) { - *error = AFErrorWithUnderlyingError(serializationError, *error); - } - - return responseObject; -} - -#pragma mark - NSSecureCoding - -- (instancetype)initWithCoder:(NSCoder *)decoder { - self = [super initWithCoder:decoder]; - if (!self) { - return nil; - } - - self.readingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readingOptions))] unsignedIntegerValue]; - self.removesKeysWithNullValues = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))] boolValue]; - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [super encodeWithCoder:coder]; - - [coder encodeObject:@(self.readingOptions) forKey:NSStringFromSelector(@selector(readingOptions))]; - [coder encodeObject:@(self.removesKeysWithNullValues) forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; - serializer.readingOptions = self.readingOptions; - serializer.removesKeysWithNullValues = self.removesKeysWithNullValues; - - return serializer; -} - -@end - -#pragma mark - - -@implementation AFXMLParserResponseSerializer - -+ (instancetype)serializer { - AFXMLParserResponseSerializer *serializer = [[self alloc] init]; - - return serializer; -} - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - - self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; - - return self; -} - -#pragma mark - AFURLResponseSerialization - -- (id)responseObjectForResponse:(NSHTTPURLResponse *)response - data:(NSData *)data - error:(NSError *__autoreleasing *)error -{ - if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { - if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { - return nil; - } - } - - return [[NSXMLParser alloc] initWithData:data]; -} - -@end - -#pragma mark - - -#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED - -@implementation AFXMLDocumentResponseSerializer - -+ (instancetype)serializer { - return [self serializerWithXMLDocumentOptions:0]; -} - -+ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask { - AFXMLDocumentResponseSerializer *serializer = [[self alloc] init]; - serializer.options = mask; - - return serializer; -} - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - - self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; - - return self; -} - -#pragma mark - AFURLResponseSerialization - -- (id)responseObjectForResponse:(NSURLResponse *)response - data:(NSData *)data - error:(NSError *__autoreleasing *)error -{ - if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { - if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { - return nil; - } - } - - NSError *serializationError = nil; - NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError]; - - if (error) { - *error = AFErrorWithUnderlyingError(serializationError, *error); - } - - return document; -} - -#pragma mark - NSSecureCoding - -- (instancetype)initWithCoder:(NSCoder *)decoder { - self = [super initWithCoder:decoder]; - if (!self) { - return nil; - } - - self.options = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(options))] unsignedIntegerValue]; - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [super encodeWithCoder:coder]; - - [coder encodeObject:@(self.options) forKey:NSStringFromSelector(@selector(options))]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFXMLDocumentResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; - serializer.options = self.options; - - return serializer; -} - -@end - -#endif - -#pragma mark - - -@implementation AFPropertyListResponseSerializer - -+ (instancetype)serializer { - return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 readOptions:0]; -} - -+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format - readOptions:(NSPropertyListReadOptions)readOptions -{ - AFPropertyListResponseSerializer *serializer = [[self alloc] init]; - serializer.format = format; - serializer.readOptions = readOptions; - - return serializer; -} - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - - self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/x-plist", nil]; - - return self; -} - -#pragma mark - AFURLResponseSerialization - -- (id)responseObjectForResponse:(NSURLResponse *)response - data:(NSData *)data - error:(NSError *__autoreleasing *)error -{ - if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { - if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { - return nil; - } - } - - id responseObject; - NSError *serializationError = nil; - - if (data) { - responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError]; - } - - if (error) { - *error = AFErrorWithUnderlyingError(serializationError, *error); - } - - return responseObject; -} - -#pragma mark - NSSecureCoding - -- (instancetype)initWithCoder:(NSCoder *)decoder { - self = [super initWithCoder:decoder]; - if (!self) { - return nil; - } - - self.format = (NSPropertyListFormat)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue]; - self.readOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readOptions))] unsignedIntegerValue]; - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [super encodeWithCoder:coder]; - - [coder encodeObject:@(self.format) forKey:NSStringFromSelector(@selector(format))]; - [coder encodeObject:@(self.readOptions) forKey:NSStringFromSelector(@selector(readOptions))]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; - serializer.format = self.format; - serializer.readOptions = self.readOptions; - - return serializer; -} - -@end - -#pragma mark - - -#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH -#import -#import - -@interface UIImage (AFNetworkingSafeImageLoading) -+ (UIImage *)af_safeImageWithData:(NSData *)data; -@end - -static NSLock* imageLock = nil; - -@implementation UIImage (AFNetworkingSafeImageLoading) - -+ (UIImage *)af_safeImageWithData:(NSData *)data { - UIImage* image = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - imageLock = [[NSLock alloc] init]; - }); - - [imageLock lock]; - image = [UIImage imageWithData:data]; - [imageLock unlock]; - return image; -} - -@end - -static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) { - UIImage *image = [UIImage af_safeImageWithData:data]; - if (image.images) { - return image; - } - - return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation]; -} - -static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) { - if (!data || [data length] == 0) { - return nil; - } - - CGImageRef imageRef = NULL; - CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); - - if ([response.MIMEType isEqualToString:@"image/png"]) { - imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); - } else if ([response.MIMEType isEqualToString:@"image/jpeg"]) { - imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); - - if (imageRef) { - CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef); - CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace); - - // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale - if (imageColorSpaceModel == kCGColorSpaceModelCMYK) { - CGImageRelease(imageRef); - imageRef = NULL; - } - } - } - - CGDataProviderRelease(dataProvider); - - UIImage *image = AFImageWithDataAtScale(data, scale); - if (!imageRef) { - if (image.images || !image) { - return image; - } - - imageRef = CGImageCreateCopy([image CGImage]); - if (!imageRef) { - return nil; - } - } - - size_t width = CGImageGetWidth(imageRef); - size_t height = CGImageGetHeight(imageRef); - size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef); - - if (width * height > 1024 * 1024 || bitsPerComponent > 8) { - CGImageRelease(imageRef); - - return image; - } - - // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate - size_t bytesPerRow = 0; - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); - CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); - - if (colorSpaceModel == kCGColorSpaceModelRGB) { - uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask); -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wassign-enum" - if (alpha == kCGImageAlphaNone) { - bitmapInfo &= ~kCGBitmapAlphaInfoMask; - bitmapInfo |= kCGImageAlphaNoneSkipFirst; - } else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) { - bitmapInfo &= ~kCGBitmapAlphaInfoMask; - bitmapInfo |= kCGImageAlphaPremultipliedFirst; - } -#pragma clang diagnostic pop - } - - CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo); - - CGColorSpaceRelease(colorSpace); - - if (!context) { - CGImageRelease(imageRef); - - return image; - } - - CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef); - CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context); - - CGContextRelease(context); - - UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation]; - - CGImageRelease(inflatedImageRef); - CGImageRelease(imageRef); - - return inflatedImage; -} -#endif - - -@implementation AFImageResponseSerializer - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - - self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil]; - -#if TARGET_OS_IOS || TARGET_OS_TV - self.imageScale = [[UIScreen mainScreen] scale]; - self.automaticallyInflatesResponseImage = YES; -#elif TARGET_OS_WATCH - self.imageScale = [[WKInterfaceDevice currentDevice] screenScale]; - self.automaticallyInflatesResponseImage = YES; -#endif - - return self; -} - -#pragma mark - AFURLResponseSerializer - -- (id)responseObjectForResponse:(NSURLResponse *)response - data:(NSData *)data - error:(NSError *__autoreleasing *)error -{ - if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { - if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { - return nil; - } - } - -#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH - if (self.automaticallyInflatesResponseImage) { - return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale); - } else { - return AFImageWithDataAtScale(data, self.imageScale); - } -#else - // Ensure that the image is set to it's correct pixel width and height - NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data]; - NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])]; - [image addRepresentation:bitimage]; - - return image; -#endif - - return nil; -} - -#pragma mark - NSSecureCoding - -- (instancetype)initWithCoder:(NSCoder *)decoder { - self = [super initWithCoder:decoder]; - if (!self) { - return nil; - } - -#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH - NSNumber *imageScale = [decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(imageScale))]; -#if CGFLOAT_IS_DOUBLE - self.imageScale = [imageScale doubleValue]; -#else - self.imageScale = [imageScale floatValue]; -#endif - - self.automaticallyInflatesResponseImage = [decoder decodeBoolForKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; -#endif - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [super encodeWithCoder:coder]; - -#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH - [coder encodeObject:@(self.imageScale) forKey:NSStringFromSelector(@selector(imageScale))]; - [coder encodeBool:self.automaticallyInflatesResponseImage forKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; -#endif -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; - -#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH - serializer.imageScale = self.imageScale; - serializer.automaticallyInflatesResponseImage = self.automaticallyInflatesResponseImage; -#endif - - return serializer; -} - -@end - -#pragma mark - - -@interface AFCompoundResponseSerializer () -@property (readwrite, nonatomic, copy) NSArray *responseSerializers; -@end - -@implementation AFCompoundResponseSerializer - -+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers { - AFCompoundResponseSerializer *serializer = [[self alloc] init]; - serializer.responseSerializers = responseSerializers; - - return serializer; -} - -#pragma mark - AFURLResponseSerialization - -- (id)responseObjectForResponse:(NSURLResponse *)response - data:(NSData *)data - error:(NSError *__autoreleasing *)error -{ - for (id serializer in self.responseSerializers) { - if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) { - continue; - } - - NSError *serializerError = nil; - id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError]; - if (responseObject) { - if (error) { - *error = AFErrorWithUnderlyingError(serializerError, *error); - } - - return responseObject; - } - } - - return [super responseObjectForResponse:response data:data error:error]; -} - -#pragma mark - NSSecureCoding - -- (instancetype)initWithCoder:(NSCoder *)decoder { - self = [super initWithCoder:decoder]; - if (!self) { - return nil; - } - - self.responseSerializers = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(responseSerializers))]; - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [super encodeWithCoder:coder]; - - [coder encodeObject:self.responseSerializers forKey:NSStringFromSelector(@selector(responseSerializers))]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - AFCompoundResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; - serializer.responseSerializers = self.responseSerializers; - - return serializer; -} - -@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h deleted file mode 100644 index 89909fe..0000000 --- a/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h +++ /dev/null @@ -1,500 +0,0 @@ -// AFURLSessionManager.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - - -#import - -#import "AFURLResponseSerialization.h" -#import "AFURLRequestSerialization.h" -#import "AFSecurityPolicy.h" -#if !TARGET_OS_WATCH -#import "AFNetworkReachabilityManager.h" -#endif - -/** - `AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. - - ## Subclassing Notes - - This is the base class for `AFHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `AFURLSessionManager` specifically for HTTP, consider subclassing `AFHTTPSessionManager` instead. - - ## NSURLSession & NSURLSessionTask Delegate Methods - - `AFURLSessionManager` implements the following delegate methods: - - ### `NSURLSessionDelegate` - - - `URLSession:didBecomeInvalidWithError:` - - `URLSession:didReceiveChallenge:completionHandler:` - - `URLSessionDidFinishEventsForBackgroundURLSession:` - - ### `NSURLSessionTaskDelegate` - - - `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:` - - `URLSession:task:didReceiveChallenge:completionHandler:` - - `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:` - - `URLSession:task:needNewBodyStream:` - - `URLSession:task:didCompleteWithError:` - - ### `NSURLSessionDataDelegate` - - - `URLSession:dataTask:didReceiveResponse:completionHandler:` - - `URLSession:dataTask:didBecomeDownloadTask:` - - `URLSession:dataTask:didReceiveData:` - - `URLSession:dataTask:willCacheResponse:completionHandler:` - - ### `NSURLSessionDownloadDelegate` - - - `URLSession:downloadTask:didFinishDownloadingToURL:` - - `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:` - - `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:` - - If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first. - - ## Network Reachability Monitoring - - Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. - - ## NSCoding Caveats - - - Encoded managers do not include any block properties. Be sure to set delegate callback blocks when using `-initWithCoder:` or `NSKeyedUnarchiver`. - - ## NSCopying Caveats - - - `-copy` and `-copyWithZone:` return a new manager with a new `NSURLSession` created from the configuration of the original. - - Operation copies do not include any delegate callback blocks, as they often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ session manager when copied. - - @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance. - */ - -NS_ASSUME_NONNULL_BEGIN - -@interface AFURLSessionManager : NSObject - -/** - The managed session. - */ -@property (readonly, nonatomic, strong) NSURLSession *session; - -/** - The operation queue on which delegate callbacks are run. - */ -@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue; - -/** - Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. - - @warning `responseSerializer` must not be `nil`. - */ -@property (nonatomic, strong) id responseSerializer; - -///------------------------------- -/// @name Managing Security Policy -///------------------------------- - -/** - The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. - */ -@property (nonatomic, strong) AFSecurityPolicy *securityPolicy; - -#if !TARGET_OS_WATCH -///-------------------------------------- -/// @name Monitoring Network Reachability -///-------------------------------------- - -/** - The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default. - */ -@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager; -#endif - -///---------------------------- -/// @name Getting Session Tasks -///---------------------------- - -/** - The data, upload, and download tasks currently run by the managed session. - */ -@property (readonly, nonatomic, strong) NSArray *tasks; - -/** - The data tasks currently run by the managed session. - */ -@property (readonly, nonatomic, strong) NSArray *dataTasks; - -/** - The upload tasks currently run by the managed session. - */ -@property (readonly, nonatomic, strong) NSArray *uploadTasks; - -/** - The download tasks currently run by the managed session. - */ -@property (readonly, nonatomic, strong) NSArray *downloadTasks; - -///------------------------------- -/// @name Managing Callback Queues -///------------------------------- - -/** - The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used. - */ -@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue; - -/** - The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. - */ -@property (nonatomic, strong, nullable) dispatch_group_t completionGroup; - -///--------------------------------- -/// @name Working Around System Bugs -///--------------------------------- - -/** - Whether to attempt to retry creation of upload tasks for background sessions when initial call returns `nil`. `NO` by default. - - @bug As of iOS 7.0, there is a bug where upload tasks created for background tasks are sometimes `nil`. As a workaround, if this property is `YES`, AFNetworking will follow Apple's recommendation to try creating the task again. - - @see https://github.com/AFNetworking/AFNetworking/issues/1675 - */ -@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions; - -///--------------------- -/// @name Initialization -///--------------------- - -/** - Creates and returns a manager for a session created with the specified configuration. This is the designated initializer. - - @param configuration The configuration used to create the managed session. - - @return A manager for a newly-created session. - */ -- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; - -/** - Invalidates the managed session, optionally canceling pending tasks. - - @param cancelPendingTasks Whether or not to cancel pending tasks. - */ -- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks; - -///------------------------- -/// @name Running Data Tasks -///------------------------- - -/** - Creates an `NSURLSessionDataTask` with the specified request. - - @param request The HTTP request for the request. - @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. - */ -- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request - completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; - -/** - Creates an `NSURLSessionDataTask` with the specified request. - - @param request The HTTP request for the request. - @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. - @param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. - @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. - */ -- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request - uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock - downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock - completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; - -///--------------------------- -/// @name Running Upload Tasks -///--------------------------- - -/** - Creates an `NSURLSessionUploadTask` with the specified request for a local file. - - @param request The HTTP request for the request. - @param fileURL A URL to the local file to be uploaded. - @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. - @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. - - @see `attemptsToRecreateUploadTasksForBackgroundSessions` - */ -- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request - fromFile:(NSURL *)fileURL - progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock - completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; - -/** - Creates an `NSURLSessionUploadTask` with the specified request for an HTTP body. - - @param request The HTTP request for the request. - @param bodyData A data object containing the HTTP body to be uploaded. - @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. - @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. - */ -- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request - fromData:(nullable NSData *)bodyData - progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock - completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; - -/** - Creates an `NSURLSessionUploadTask` with the specified streaming request. - - @param request The HTTP request for the request. - @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. - @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. - */ -- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request - progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock - completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; - -///----------------------------- -/// @name Running Download Tasks -///----------------------------- - -/** - Creates an `NSURLSessionDownloadTask` with the specified request. - - @param request The HTTP request for the request. - @param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. - @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL. - @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any. - - @warning If using a background `NSURLSessionConfiguration` on iOS, these blocks will be lost when the app is terminated. Background sessions may prefer to use `-setDownloadTaskDidFinishDownloadingBlock:` to specify the URL for saving the downloaded file, rather than the destination block of this method. - */ -- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request - progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock - destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination - completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler; - -/** - Creates an `NSURLSessionDownloadTask` with the specified resume data. - - @param resumeData The data used to resume downloading. - @param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. - @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL. - @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any. - */ -- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData - progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock - destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination - completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler; - -///--------------------------------- -/// @name Getting Progress for Tasks -///--------------------------------- - -/** - Returns the upload progress of the specified task. - - @param task The session task. Must not be `nil`. - - @return An `NSProgress` object reporting the upload progress of a task, or `nil` if the progress is unavailable. - */ -- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task; - -/** - Returns the download progress of the specified task. - - @param task The session task. Must not be `nil`. - - @return An `NSProgress` object reporting the download progress of a task, or `nil` if the progress is unavailable. - */ -- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task; - -///----------------------------------------- -/// @name Setting Session Delegate Callbacks -///----------------------------------------- - -/** - Sets a block to be executed when the managed session becomes invalid, as handled by the `NSURLSessionDelegate` method `URLSession:didBecomeInvalidWithError:`. - - @param block A block object to be executed when the managed session becomes invalid. The block has no return value, and takes two arguments: the session, and the error related to the cause of invalidation. - */ -- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block; - -/** - Sets a block to be executed when a connection level authentication challenge has occurred, as handled by the `NSURLSessionDelegate` method `URLSession:didReceiveChallenge:completionHandler:`. - - @param block A block object to be executed when a connection level authentication challenge has occurred. The block returns the disposition of the authentication challenge, and takes three arguments: the session, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge. - */ -- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block; - -///-------------------------------------- -/// @name Setting Task Delegate Callbacks -///-------------------------------------- - -/** - Sets a block to be executed when a task requires a new request body stream to send to the remote server, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:needNewBodyStream:`. - - @param block A block object to be executed when a task requires a new request body stream. - */ -- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block; - -/** - Sets a block to be executed when an HTTP request is attempting to perform a redirection to a different URL, as handled by the `NSURLSessionTaskDelegate` method `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`. - - @param block A block object to be executed when an HTTP request is attempting to perform a redirection to a different URL. The block returns the request to be made for the redirection, and takes four arguments: the session, the task, the redirection response, and the request corresponding to the redirection response. - */ -- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block; - -/** - Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`. - - @param block A block object to be executed when a session task has received a request specific authentication challenge. The block returns the disposition of the authentication challenge, and takes four arguments: the session, the task, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge. - */ -- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block; - -/** - Sets a block to be executed periodically to track upload progress, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`. - - @param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes five arguments: the session, the task, the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. - */ -- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block; - -/** - Sets a block to be executed as the last message related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didCompleteWithError:`. - - @param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any error that occurred in the process of executing the task. - */ -- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block; - -///------------------------------------------- -/// @name Setting Data Task Delegate Callbacks -///------------------------------------------- - -/** - Sets a block to be executed when a data task has received a response, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveResponse:completionHandler:`. - - @param block A block object to be executed when a data task has received a response. The block returns the disposition of the session response, and takes three arguments: the session, the data task, and the received response. - */ -- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block; - -/** - Sets a block to be executed when a data task has become a download task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didBecomeDownloadTask:`. - - @param block A block object to be executed when a data task has become a download task. The block has no return value, and takes three arguments: the session, the data task, and the download task it has become. - */ -- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block; - -/** - Sets a block to be executed when a data task receives data, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveData:`. - - @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the session, the data task, and the data received. This block may be called multiple times, and will execute on the session manager operation queue. - */ -- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block; - -/** - Sets a block to be executed to determine the caching behavior of a data task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:willCacheResponse:completionHandler:`. - - @param block A block object to be executed to determine the caching behavior of a data task. The block returns the response to cache, and takes three arguments: the session, the data task, and the proposed cached URL response. - */ -- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block; - -/** - Sets a block to be executed once all messages enqueued for a session have been delivered, as handled by the `NSURLSessionDataDelegate` method `URLSessionDidFinishEventsForBackgroundURLSession:`. - - @param block A block object to be executed once all messages enqueued for a session have been delivered. The block has no return value and takes a single argument: the session. - */ -- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block; - -///----------------------------------------------- -/// @name Setting Download Task Delegate Callbacks -///----------------------------------------------- - -/** - Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`. - - @param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error. - */ -- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block; - -/** - Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`. - - @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the session, the download task, the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the session manager operation queue. - */ -- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block; - -/** - Sets a block to be executed when a download task has been resumed, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`. - - @param block A block object to be executed when a download task has been resumed. The block has no return value and takes four arguments: the session, the download task, the file offset of the resumed download, and the total number of bytes expected to be downloaded. - */ -- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block; - -@end - -///-------------------- -/// @name Notifications -///-------------------- - -/** - Posted when a task resumes. - */ -FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidResumeNotification; - -/** - Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task. - */ -FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteNotification; - -/** - Posted when a task suspends its execution. - */ -FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification; - -/** - Posted when a session is invalidated. - */ -FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification; - -/** - Posted when a session download task encountered an error when moving the temporary download file to a specified destination. - */ -FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification; - -/** - The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if response data exists for the task. - */ -FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseDataKey; - -/** - The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the response was serialized. - */ -FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey; - -/** - The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the task has an associated response serializer. - */ -FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey; - -/** - The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an the response data has been stored directly to disk. - */ -FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteAssetPathKey; - -/** - Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an error exists. - */ -FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteErrorKey; - -NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m deleted file mode 100644 index ef2108c..0000000 --- a/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m +++ /dev/null @@ -1,1244 +0,0 @@ -// AFURLSessionManager.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "AFURLSessionManager.h" -#import - -#ifndef NSFoundationVersionNumber_iOS_8_0 -#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11 -#else -#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0 -#endif - -static dispatch_queue_t url_session_manager_creation_queue() { - static dispatch_queue_t af_url_session_manager_creation_queue; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL); - }); - - return af_url_session_manager_creation_queue; -} - -static void url_session_manager_create_task_safely(dispatch_block_t block) { - if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) { - // Fix of bug - // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8) - // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093 - dispatch_sync(url_session_manager_creation_queue(), block); - } else { - block(); - } -} - -static dispatch_queue_t url_session_manager_processing_queue() { - static dispatch_queue_t af_url_session_manager_processing_queue; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT); - }); - - return af_url_session_manager_processing_queue; -} - -static dispatch_group_t url_session_manager_completion_group() { - static dispatch_group_t af_url_session_manager_completion_group; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - af_url_session_manager_completion_group = dispatch_group_create(); - }); - - return af_url_session_manager_completion_group; -} - -NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume"; -NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete"; -NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend"; -NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate"; -NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error"; - -NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; -NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; -NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; -NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error"; -NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; - -static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock"; - -static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3; - -static void * AFTaskStateChangedContext = &AFTaskStateChangedContext; - -typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error); -typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); - -typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request); -typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); -typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session); - -typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task); -typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend); -typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error); - -typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response); -typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask); -typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data); -typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse); - -typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location); -typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite); -typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes); -typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *); - -typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error); - - -#pragma mark - - -@interface AFURLSessionManagerTaskDelegate : NSObject -@property (nonatomic, weak) AFURLSessionManager *manager; -@property (nonatomic, strong) NSMutableData *mutableData; -@property (nonatomic, strong) NSProgress *uploadProgress; -@property (nonatomic, strong) NSProgress *downloadProgress; -@property (nonatomic, copy) NSURL *downloadFileURL; -@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; -@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; -@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; -@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; -@end - -@implementation AFURLSessionManagerTaskDelegate - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - - self.mutableData = [NSMutableData data]; - self.uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; - self.uploadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown; - - self.downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; - self.downloadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown; - return self; -} - -#pragma mark - NSProgress Tracking - -- (void)setupProgressForTask:(NSURLSessionTask *)task { - __weak __typeof__(task) weakTask = task; - - self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend; - self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive; - [self.uploadProgress setCancellable:YES]; - [self.uploadProgress setCancellationHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask cancel]; - }]; - [self.uploadProgress setPausable:YES]; - [self.uploadProgress setPausingHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask suspend]; - }]; - if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) { - [self.uploadProgress setResumingHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask resume]; - }]; - } - - [self.downloadProgress setCancellable:YES]; - [self.downloadProgress setCancellationHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask cancel]; - }]; - [self.downloadProgress setPausable:YES]; - [self.downloadProgress setPausingHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask suspend]; - }]; - - if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) { - [self.downloadProgress setResumingHandler:^{ - __typeof__(weakTask) strongTask = weakTask; - [strongTask resume]; - }]; - } - - [task addObserver:self - forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived)) - options:NSKeyValueObservingOptionNew - context:NULL]; - [task addObserver:self - forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive)) - options:NSKeyValueObservingOptionNew - context:NULL]; - - [task addObserver:self - forKeyPath:NSStringFromSelector(@selector(countOfBytesSent)) - options:NSKeyValueObservingOptionNew - context:NULL]; - [task addObserver:self - forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend)) - options:NSKeyValueObservingOptionNew - context:NULL]; - - [self.downloadProgress addObserver:self - forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) - options:NSKeyValueObservingOptionNew - context:NULL]; - [self.uploadProgress addObserver:self - forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) - options:NSKeyValueObservingOptionNew - context:NULL]; -} - -- (void)cleanUpProgressForTask:(NSURLSessionTask *)task { - [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))]; - [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]; - [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))]; - [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]; - [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; - [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) { - if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) { - self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; - } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) { - self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; - } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) { - self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; - } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) { - self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; - } - } - else if ([object isEqual:self.downloadProgress]) { - if (self.downloadProgressBlock) { - self.downloadProgressBlock(object); - } - } - else if ([object isEqual:self.uploadProgress]) { - if (self.uploadProgressBlock) { - self.uploadProgressBlock(object); - } - } -} - -#pragma mark - NSURLSessionTaskDelegate - -- (void)URLSession:(__unused NSURLSession *)session - task:(NSURLSessionTask *)task -didCompleteWithError:(NSError *)error -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" - __strong AFURLSessionManager *manager = self.manager; - - __block id responseObject = nil; - - __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; - - //Performance Improvement from #2672 - NSData *data = nil; - if (self.mutableData) { - data = [self.mutableData copy]; - //We no longer need the reference, so nil it out to gain back some memory. - self.mutableData = nil; - } - - if (self.downloadFileURL) { - userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL; - } else if (data) { - userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data; - } - - if (error) { - userInfo[AFNetworkingTaskDidCompleteErrorKey] = error; - - dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ - if (self.completionHandler) { - self.completionHandler(task.response, responseObject, error); - } - - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; - }); - }); - } else { - dispatch_async(url_session_manager_processing_queue(), ^{ - NSError *serializationError = nil; - responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]; - - if (self.downloadFileURL) { - responseObject = self.downloadFileURL; - } - - if (responseObject) { - userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject; - } - - if (serializationError) { - userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError; - } - - dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ - if (self.completionHandler) { - self.completionHandler(task.response, responseObject, serializationError); - } - - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; - }); - }); - }); - } -#pragma clang diagnostic pop -} - -#pragma mark - NSURLSessionDataTaskDelegate - -- (void)URLSession:(__unused NSURLSession *)session - dataTask:(__unused NSURLSessionDataTask *)dataTask - didReceiveData:(NSData *)data -{ - [self.mutableData appendData:data]; -} - -#pragma mark - NSURLSessionDownloadTaskDelegate - -- (void)URLSession:(NSURLSession *)session - downloadTask:(NSURLSessionDownloadTask *)downloadTask -didFinishDownloadingToURL:(NSURL *)location -{ - NSError *fileManagerError = nil; - self.downloadFileURL = nil; - - if (self.downloadTaskDidFinishDownloading) { - self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); - if (self.downloadFileURL) { - [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]; - - if (fileManagerError) { - [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; - } - } - } -} - -@end - -#pragma mark - - -/** - * A workaround for issues related to key-value observing the `state` of an `NSURLSessionTask`. - * - * See: - * - https://github.com/AFNetworking/AFNetworking/issues/1477 - * - https://github.com/AFNetworking/AFNetworking/issues/2638 - * - https://github.com/AFNetworking/AFNetworking/pull/2702 - */ - -static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) { - Method originalMethod = class_getInstanceMethod(theClass, originalSelector); - Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); -} - -static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) { - return class_addMethod(theClass, selector, method_getImplementation(method), method_getTypeEncoding(method)); -} - -static NSString * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume"; -static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend"; - -@interface _AFURLSessionTaskSwizzling : NSObject - -@end - -@implementation _AFURLSessionTaskSwizzling - -+ (void)load { - /** - WARNING: Trouble Ahead - https://github.com/AFNetworking/AFNetworking/pull/2702 - */ - - if (NSClassFromString(@"NSURLSessionTask")) { - /** - iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky. - Many Unit Tests have been built to validate as much of this behavior has possible. - Here is what we know: - - NSURLSessionTasks are implemented with class clusters, meaning the class you request from the API isn't actually the type of class you will get back. - - Simply referencing `[NSURLSessionTask class]` will not work. You need to ask an `NSURLSession` to actually create an object, and grab the class from there. - - On iOS 7, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `__NSCFURLSessionTask`. - - On iOS 8, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `NSURLSessionTask`. - - On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled. - - On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled. - - Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there. - - Some Assumptions: - - No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it. - - No background task classes override `resume` or `suspend` - - The current solution: - 1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task. - 2) Grab a pointer to the original implementation of `af_resume` - 3) Check to see if the current class has an implementation of resume. If so, continue to step 4. - 4) Grab the super class of the current class. - 5) Grab a pointer for the current class to the current implementation of `resume`. - 6) Grab a pointer for the super class to the current implementation of `resume`. - 7) If the current class implementation of `resume` is not equal to the super class implementation of `resume` AND the current implementation of `resume` is not equal to the original implementation of `af_resume`, THEN swizzle the methods - 8) Set the current class to the super class, and repeat steps 3-8 - */ - NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; - NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration]; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnonnull" - NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil]; -#pragma clang diagnostic pop - IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume))); - Class currentClass = [localDataTask class]; - - while (class_getInstanceMethod(currentClass, @selector(resume))) { - Class superClass = [currentClass superclass]; - IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume))); - IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume))); - if (classResumeIMP != superclassResumeIMP && - originalAFResumeIMP != classResumeIMP) { - [self swizzleResumeAndSuspendMethodForClass:currentClass]; - } - currentClass = [currentClass superclass]; - } - - [localDataTask cancel]; - [session finishTasksAndInvalidate]; - } -} - -+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass { - Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume)); - Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend)); - - if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) { - af_swizzleSelector(theClass, @selector(resume), @selector(af_resume)); - } - - if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) { - af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend)); - } -} - -- (NSURLSessionTaskState)state { - NSAssert(NO, @"State method should never be called in the actual dummy class"); - return NSURLSessionTaskStateCanceling; -} - -- (void)af_resume { - NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); - NSURLSessionTaskState state = [self state]; - [self af_resume]; - - if (state != NSURLSessionTaskStateRunning) { - [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; - } -} - -- (void)af_suspend { - NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); - NSURLSessionTaskState state = [self state]; - [self af_suspend]; - - if (state != NSURLSessionTaskStateSuspended) { - [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self]; - } -} -@end - -#pragma mark - - -@interface AFURLSessionManager () -@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration; -@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; -@property (readwrite, nonatomic, strong) NSURLSession *session; -@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; -@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks; -@property (readwrite, nonatomic, strong) NSLock *lock; -@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid; -@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; -@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession; -@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection; -@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge; -@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream; -@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData; -@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete; -@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse; -@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask; -@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData; -@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse; -@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; -@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData; -@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume; -@end - -@implementation AFURLSessionManager - -- (instancetype)init { - return [self initWithSessionConfiguration:nil]; -} - -- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { - self = [super init]; - if (!self) { - return nil; - } - - if (!configuration) { - configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; - } - - self.sessionConfiguration = configuration; - - self.operationQueue = [[NSOperationQueue alloc] init]; - self.operationQueue.maxConcurrentOperationCount = 1; - - self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; - - self.responseSerializer = [AFJSONResponseSerializer serializer]; - - self.securityPolicy = [AFSecurityPolicy defaultPolicy]; - -#if !TARGET_OS_WATCH - self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; -#endif - - self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; - - self.lock = [[NSLock alloc] init]; - self.lock.name = AFURLSessionManagerLockName; - - [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { - for (NSURLSessionDataTask *task in dataTasks) { - [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil]; - } - - for (NSURLSessionUploadTask *uploadTask in uploadTasks) { - [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil]; - } - - for (NSURLSessionDownloadTask *downloadTask in downloadTasks) { - [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil]; - } - }]; - - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -#pragma mark - - -- (NSString *)taskDescriptionForSessionTasks { - return [NSString stringWithFormat:@"%p", self]; -} - -- (void)taskDidResume:(NSNotification *)notification { - NSURLSessionTask *task = notification.object; - if ([task respondsToSelector:@selector(taskDescription)]) { - if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task]; - }); - } - } -} - -- (void)taskDidSuspend:(NSNotification *)notification { - NSURLSessionTask *task = notification.object; - if ([task respondsToSelector:@selector(taskDescription)]) { - if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task]; - }); - } - } -} - -#pragma mark - - -- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task { - NSParameterAssert(task); - - AFURLSessionManagerTaskDelegate *delegate = nil; - [self.lock lock]; - delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)]; - [self.lock unlock]; - - return delegate; -} - -- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate - forTask:(NSURLSessionTask *)task -{ - NSParameterAssert(task); - NSParameterAssert(delegate); - - [self.lock lock]; - self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; - [delegate setupProgressForTask:task]; - [self addNotificationObserverForTask:task]; - [self.lock unlock]; -} - -- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask - uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock - downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock - completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler -{ - AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; - delegate.manager = self; - delegate.completionHandler = completionHandler; - - dataTask.taskDescription = self.taskDescriptionForSessionTasks; - [self setDelegate:delegate forTask:dataTask]; - - delegate.uploadProgressBlock = uploadProgressBlock; - delegate.downloadProgressBlock = downloadProgressBlock; -} - -- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask - progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock - completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler -{ - AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; - delegate.manager = self; - delegate.completionHandler = completionHandler; - - uploadTask.taskDescription = self.taskDescriptionForSessionTasks; - - [self setDelegate:delegate forTask:uploadTask]; - - delegate.uploadProgressBlock = uploadProgressBlock; -} - -- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask - progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock - destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination - completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler -{ - AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; - delegate.manager = self; - delegate.completionHandler = completionHandler; - - if (destination) { - delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) { - return destination(location, task.response); - }; - } - - downloadTask.taskDescription = self.taskDescriptionForSessionTasks; - - [self setDelegate:delegate forTask:downloadTask]; - - delegate.downloadProgressBlock = downloadProgressBlock; -} - -- (void)removeDelegateForTask:(NSURLSessionTask *)task { - NSParameterAssert(task); - - AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; - [self.lock lock]; - [delegate cleanUpProgressForTask:task]; - [self removeNotificationObserverForTask:task]; - [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; - [self.lock unlock]; -} - -#pragma mark - - -- (NSArray *)tasksForKeyPath:(NSString *)keyPath { - __block NSArray *tasks = nil; - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { - if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) { - tasks = dataTasks; - } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) { - tasks = uploadTasks; - } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) { - tasks = downloadTasks; - } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) { - tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"]; - } - - dispatch_semaphore_signal(semaphore); - }]; - - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - - return tasks; -} - -- (NSArray *)tasks { - return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; -} - -- (NSArray *)dataTasks { - return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; -} - -- (NSArray *)uploadTasks { - return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; -} - -- (NSArray *)downloadTasks { - return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; -} - -#pragma mark - - -- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks { - dispatch_async(dispatch_get_main_queue(), ^{ - if (cancelPendingTasks) { - [self.session invalidateAndCancel]; - } else { - [self.session finishTasksAndInvalidate]; - } - }); -} - -#pragma mark - - -- (void)setResponseSerializer:(id )responseSerializer { - NSParameterAssert(responseSerializer); - - _responseSerializer = responseSerializer; -} - -#pragma mark - -- (void)addNotificationObserverForTask:(NSURLSessionTask *)task { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task]; -} - -- (void)removeNotificationObserverForTask:(NSURLSessionTask *)task { - [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task]; -} - -#pragma mark - - -- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request - completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler -{ - return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler]; -} - -- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request - uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock - downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock - completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler { - - __block NSURLSessionDataTask *dataTask = nil; - url_session_manager_create_task_safely(^{ - dataTask = [self.session dataTaskWithRequest:request]; - }); - - [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler]; - - return dataTask; -} - -#pragma mark - - -- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request - fromFile:(NSURL *)fileURL - progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock - completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler -{ - __block NSURLSessionUploadTask *uploadTask = nil; - url_session_manager_create_task_safely(^{ - uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; - }); - - if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) { - for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) { - uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; - } - } - - [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; - - return uploadTask; -} - -- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request - fromData:(NSData *)bodyData - progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock - completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler -{ - __block NSURLSessionUploadTask *uploadTask = nil; - url_session_manager_create_task_safely(^{ - uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; - }); - - [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; - - return uploadTask; -} - -- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request - progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock - completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler -{ - __block NSURLSessionUploadTask *uploadTask = nil; - url_session_manager_create_task_safely(^{ - uploadTask = [self.session uploadTaskWithStreamedRequest:request]; - }); - - [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; - - return uploadTask; -} - -#pragma mark - - -- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request - progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock - destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination - completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler -{ - __block NSURLSessionDownloadTask *downloadTask = nil; - url_session_manager_create_task_safely(^{ - downloadTask = [self.session downloadTaskWithRequest:request]; - }); - - [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler]; - - return downloadTask; -} - -- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData - progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock - destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination - completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler -{ - __block NSURLSessionDownloadTask *downloadTask = nil; - url_session_manager_create_task_safely(^{ - downloadTask = [self.session downloadTaskWithResumeData:resumeData]; - }); - - [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler]; - - return downloadTask; -} - -#pragma mark - -- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task { - return [[self delegateForTask:task] uploadProgress]; -} - -- (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task { - return [[self delegateForTask:task] downloadProgress]; -} - -#pragma mark - - -- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block { - self.sessionDidBecomeInvalid = block; -} - -- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { - self.sessionDidReceiveAuthenticationChallenge = block; -} - -- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block { - self.didFinishEventsForBackgroundURLSession = block; -} - -#pragma mark - - -- (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block { - self.taskNeedNewBodyStream = block; -} - -- (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block { - self.taskWillPerformHTTPRedirection = block; -} - -- (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { - self.taskDidReceiveAuthenticationChallenge = block; -} - -- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block { - self.taskDidSendBodyData = block; -} - -- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block { - self.taskDidComplete = block; -} - -#pragma mark - - -- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block { - self.dataTaskDidReceiveResponse = block; -} - -- (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block { - self.dataTaskDidBecomeDownloadTask = block; -} - -- (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block { - self.dataTaskDidReceiveData = block; -} - -- (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block { - self.dataTaskWillCacheResponse = block; -} - -#pragma mark - - -- (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block { - self.downloadTaskDidFinishDownloading = block; -} - -- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block { - self.downloadTaskDidWriteData = block; -} - -- (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block { - self.downloadTaskDidResume = block; -} - -#pragma mark - NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue]; -} - -- (BOOL)respondsToSelector:(SEL)selector { - if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) { - return self.taskWillPerformHTTPRedirection != nil; - } else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) { - return self.dataTaskDidReceiveResponse != nil; - } else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) { - return self.dataTaskWillCacheResponse != nil; - } else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) { - return self.didFinishEventsForBackgroundURLSession != nil; - } - - return [[self class] instancesRespondToSelector:selector]; -} - -#pragma mark - NSURLSessionDelegate - -- (void)URLSession:(NSURLSession *)session -didBecomeInvalidWithError:(NSError *)error -{ - if (self.sessionDidBecomeInvalid) { - self.sessionDidBecomeInvalid(session, error); - } - - [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session]; -} - -- (void)URLSession:(NSURLSession *)session -didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge - completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler -{ - NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; - __block NSURLCredential *credential = nil; - - if (self.sessionDidReceiveAuthenticationChallenge) { - disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential); - } else { - if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { - if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { - credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; - if (credential) { - disposition = NSURLSessionAuthChallengeUseCredential; - } else { - disposition = NSURLSessionAuthChallengePerformDefaultHandling; - } - } else { - disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; - } - } else { - disposition = NSURLSessionAuthChallengePerformDefaultHandling; - } - } - - if (completionHandler) { - completionHandler(disposition, credential); - } -} - -#pragma mark - NSURLSessionTaskDelegate - -- (void)URLSession:(NSURLSession *)session - task:(NSURLSessionTask *)task -willPerformHTTPRedirection:(NSHTTPURLResponse *)response - newRequest:(NSURLRequest *)request - completionHandler:(void (^)(NSURLRequest *))completionHandler -{ - NSURLRequest *redirectRequest = request; - - if (self.taskWillPerformHTTPRedirection) { - redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request); - } - - if (completionHandler) { - completionHandler(redirectRequest); - } -} - -- (void)URLSession:(NSURLSession *)session - task:(NSURLSessionTask *)task -didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge - completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler -{ - NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; - __block NSURLCredential *credential = nil; - - if (self.taskDidReceiveAuthenticationChallenge) { - disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential); - } else { - if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { - if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { - disposition = NSURLSessionAuthChallengeUseCredential; - credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; - } else { - disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; - } - } else { - disposition = NSURLSessionAuthChallengePerformDefaultHandling; - } - } - - if (completionHandler) { - completionHandler(disposition, credential); - } -} - -- (void)URLSession:(NSURLSession *)session - task:(NSURLSessionTask *)task - needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler -{ - NSInputStream *inputStream = nil; - - if (self.taskNeedNewBodyStream) { - inputStream = self.taskNeedNewBodyStream(session, task); - } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) { - inputStream = [task.originalRequest.HTTPBodyStream copy]; - } - - if (completionHandler) { - completionHandler(inputStream); - } -} - -- (void)URLSession:(NSURLSession *)session - task:(NSURLSessionTask *)task - didSendBodyData:(int64_t)bytesSent - totalBytesSent:(int64_t)totalBytesSent -totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend -{ - - int64_t totalUnitCount = totalBytesExpectedToSend; - if(totalUnitCount == NSURLSessionTransferSizeUnknown) { - NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"]; - if(contentLength) { - totalUnitCount = (int64_t) [contentLength longLongValue]; - } - } - - if (self.taskDidSendBodyData) { - self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount); - } -} - -- (void)URLSession:(NSURLSession *)session - task:(NSURLSessionTask *)task -didCompleteWithError:(NSError *)error -{ - AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; - - // delegate may be nil when completing a task in the background - if (delegate) { - [delegate URLSession:session task:task didCompleteWithError:error]; - - [self removeDelegateForTask:task]; - } - - if (self.taskDidComplete) { - self.taskDidComplete(session, task, error); - } -} - -#pragma mark - NSURLSessionDataDelegate - -- (void)URLSession:(NSURLSession *)session - dataTask:(NSURLSessionDataTask *)dataTask -didReceiveResponse:(NSURLResponse *)response - completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler -{ - NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow; - - if (self.dataTaskDidReceiveResponse) { - disposition = self.dataTaskDidReceiveResponse(session, dataTask, response); - } - - if (completionHandler) { - completionHandler(disposition); - } -} - -- (void)URLSession:(NSURLSession *)session - dataTask:(NSURLSessionDataTask *)dataTask -didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask -{ - AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; - if (delegate) { - [self removeDelegateForTask:dataTask]; - [self setDelegate:delegate forTask:downloadTask]; - } - - if (self.dataTaskDidBecomeDownloadTask) { - self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask); - } -} - -- (void)URLSession:(NSURLSession *)session - dataTask:(NSURLSessionDataTask *)dataTask - didReceiveData:(NSData *)data -{ - - AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; - [delegate URLSession:session dataTask:dataTask didReceiveData:data]; - - if (self.dataTaskDidReceiveData) { - self.dataTaskDidReceiveData(session, dataTask, data); - } -} - -- (void)URLSession:(NSURLSession *)session - dataTask:(NSURLSessionDataTask *)dataTask - willCacheResponse:(NSCachedURLResponse *)proposedResponse - completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler -{ - NSCachedURLResponse *cachedResponse = proposedResponse; - - if (self.dataTaskWillCacheResponse) { - cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse); - } - - if (completionHandler) { - completionHandler(cachedResponse); - } -} - -- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { - if (self.didFinishEventsForBackgroundURLSession) { - dispatch_async(dispatch_get_main_queue(), ^{ - self.didFinishEventsForBackgroundURLSession(session); - }); - } -} - -#pragma mark - NSURLSessionDownloadDelegate - -- (void)URLSession:(NSURLSession *)session - downloadTask:(NSURLSessionDownloadTask *)downloadTask -didFinishDownloadingToURL:(NSURL *)location -{ - AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; - if (self.downloadTaskDidFinishDownloading) { - NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); - if (fileURL) { - delegate.downloadFileURL = fileURL; - NSError *error = nil; - [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; - if (error) { - [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; - } - - return; - } - } - - if (delegate) { - [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; - } -} - -- (void)URLSession:(NSURLSession *)session - downloadTask:(NSURLSessionDownloadTask *)downloadTask - didWriteData:(int64_t)bytesWritten - totalBytesWritten:(int64_t)totalBytesWritten -totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite -{ - if (self.downloadTaskDidWriteData) { - self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); - } -} - -- (void)URLSession:(NSURLSession *)session - downloadTask:(NSURLSessionDownloadTask *)downloadTask - didResumeAtOffset:(int64_t)fileOffset -expectedTotalBytes:(int64_t)expectedTotalBytes -{ - if (self.downloadTaskDidResume) { - self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes); - } -} - -#pragma mark - NSSecureCoding - -+ (BOOL)supportsSecureCoding { - return YES; -} - -- (instancetype)initWithCoder:(NSCoder *)decoder { - NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; - - self = [self initWithSessionConfiguration:configuration]; - if (!self) { - return nil; - } - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration]; -} - -@end diff --git a/Pods/AFNetworking/LICENSE b/Pods/AFNetworking/LICENSE deleted file mode 100644 index 3fbc2c9..0000000 --- a/Pods/AFNetworking/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2011–2016 Alamofire Software Foundation (http://alamofire.org/) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/Pods/AFNetworking/README.md b/Pods/AFNetworking/README.md deleted file mode 100644 index 53cb202..0000000 --- a/Pods/AFNetworking/README.md +++ /dev/null @@ -1,320 +0,0 @@ -

- AFNetworking -

- -[![Build Status](https://travis-ci.org/AFNetworking/AFNetworking.svg)](https://travis-ci.org/AFNetworking/AFNetworking) -[![codecov.io](https://codecov.io/github/AFNetworking/AFNetworking/coverage.svg?branch=master)](https://codecov.io/github/AFNetworking/AFNetworking?branch=master) -[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/AFNetworking.svg)](https://img.shields.io/cocoapods/v/AFNetworking.svg) -[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -[![Platform](https://img.shields.io/cocoapods/p/AFNetworking.svg?style=flat)](http://cocoadocs.org/docsets/AFNetworking) -[![Twitter](https://img.shields.io/badge/twitter-@AFNetworking-blue.svg?style=flat)](http://twitter.com/AFNetworking) - -AFNetworking is a delightful networking library for iOS and Mac OS X. It's built on top of the [Foundation URL Loading System](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html), extending the powerful high-level networking abstractions built into Cocoa. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use. - -Perhaps the most important feature of all, however, is the amazing community of developers who use and contribute to AFNetworking every day. AFNetworking powers some of the most popular and critically-acclaimed apps on the iPhone, iPad, and Mac. - -Choose AFNetworking for your next project, or migrate over your existing projects—you'll be happy you did! - -## How To Get Started - -- [Download AFNetworking](https://github.com/AFNetworking/AFNetworking/archive/master.zip) and try out the included Mac and iPhone example apps -- Read the ["Getting Started" guide](https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking), [FAQ](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ), or [other articles on the Wiki](https://github.com/AFNetworking/AFNetworking/wiki) -- Check out the [documentation](http://cocoadocs.org/docsets/AFNetworking/) for a comprehensive look at all of the APIs available in AFNetworking -- Read the [AFNetworking 3.0 Migration Guide](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-3.0-Migration-Guide) for an overview of the architectural changes from 2.0. - -## Communication - -- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/afnetworking). (Tag 'afnetworking') -- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/afnetworking). -- If you **found a bug**, _and can provide steps to reliably reproduce it_, open an issue. -- If you **have a feature request**, open an issue. -- If you **want to contribute**, submit a pull request. - -## Installation -AFNetworking supports multiple methods for installing the library in a project. - -## Installation with CocoaPods - -[CocoaPods](http://cocoapods.org) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like AFNetworking in your projects. See the ["Getting Started" guide for more information](https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking). You can install it with the following command: - -```bash -$ gem install cocoapods -``` - -> CocoaPods 0.39.0+ is required to build AFNetworking 3.0.0+. - -#### Podfile - -To integrate AFNetworking into your Xcode project using CocoaPods, specify it in your `Podfile`: - -```ruby -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' - -pod 'AFNetworking', '~> 3.0' -``` - -Then, run the following command: - -```bash -$ pod install -``` - -### Installation with Carthage - -[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. - -You can install Carthage with [Homebrew](http://brew.sh/) using the following command: - -```bash -$ brew update -$ brew install carthage -``` - -To integrate AFNetworking into your Xcode project using Carthage, specify it in your `Cartfile`: - -```ogdl -github "AFNetworking/AFNetworking" ~> 3.0 -``` - -Run `carthage` to build the framework and drag the built `AFNetworking.framework` into your Xcode project. - -## Requirements - -| AFNetworking Version | Minimum iOS Target | Minimum OS X Target | Minimum watchOS Target | Minimum tvOS Target | Notes | -|:--------------------:|:---------------------------:|:----------------------------:|:----------------------------:|:----------------------------:|:-------------------------------------------------------------------------:| -| 3.x | iOS 7 | OS X 10.9 | watchOS 2.0 | tvOS 9.0 | Xcode 7+ is required. `NSURLConnectionOperation` support has been removed. | -| 2.6 -> 2.6.3 | iOS 7 | OS X 10.9 | watchOS 2.0 | n/a | Xcode 7+ is required. | -| 2.0 -> 2.5.4 | iOS 6 | OS X 10.8 | n/a | n/a | Xcode 5+ is required. `NSURLSession` subspec requires iOS 7 or OS X 10.9. | -| 1.x | iOS 5 | Mac OS X 10.7 | n/a | n/a | -| 0.10.x | iOS 4 | Mac OS X 10.6 | n/a | n/a | - -(OS X projects must support [64-bit with modern Cocoa runtime](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtVersionsPlatforms.html)). - -> Programming in Swift? Try [Alamofire](https://github.com/Alamofire/Alamofire) for a more conventional set of APIs. - -## Architecture - -### NSURLSession - -- `AFURLSessionManager` -- `AFHTTPSessionManager` - -### Serialization - -* `` - - `AFHTTPRequestSerializer` - - `AFJSONRequestSerializer` - - `AFPropertyListRequestSerializer` -* `` - - `AFHTTPResponseSerializer` - - `AFJSONResponseSerializer` - - `AFXMLParserResponseSerializer` - - `AFXMLDocumentResponseSerializer` _(Mac OS X)_ - - `AFPropertyListResponseSerializer` - - `AFImageResponseSerializer` - - `AFCompoundResponseSerializer` - -### Additional Functionality - -- `AFSecurityPolicy` -- `AFNetworkReachabilityManager` - -## Usage - -### AFURLSessionManager - -`AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. - -#### Creating a Download Task - -```objective-c -NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; -AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; - -NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"]; -NSURLRequest *request = [NSURLRequest requestWithURL:URL]; - -NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { - NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil]; - return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]]; -} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { - NSLog(@"File downloaded to: %@", filePath); -}]; -[downloadTask resume]; -``` - -#### Creating an Upload Task - -```objective-c -NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; -AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; - -NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"]; -NSURLRequest *request = [NSURLRequest requestWithURL:URL]; - -NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"]; -NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { - if (error) { - NSLog(@"Error: %@", error); - } else { - NSLog(@"Success: %@ %@", response, responseObject); - } -}]; -[uploadTask resume]; -``` - -#### Creating an Upload Task for a Multi-Part Request, with Progress - -```objective-c -NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id formData) { - [formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil]; - } error:nil]; - -AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; - -NSURLSessionUploadTask *uploadTask; -uploadTask = [manager - uploadTaskWithStreamedRequest:request - progress:^(NSProgress * _Nonnull uploadProgress) { - // This is not called back on the main queue. - // You are responsible for dispatching to the main queue for UI updates - dispatch_async(dispatch_get_main_queue(), ^{ - //Update the progress view - [progressView setProgress:uploadProgress.fractionCompleted]; - }); - } - completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { - if (error) { - NSLog(@"Error: %@", error); - } else { - NSLog(@"%@ %@", response, responseObject); - } - }]; - -[uploadTask resume]; -``` - -#### Creating a Data Task - -```objective-c -NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; -AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; - -NSURL *URL = [NSURL URLWithString:@"http://httpbin.org/get"]; -NSURLRequest *request = [NSURLRequest requestWithURL:URL]; - -NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { - if (error) { - NSLog(@"Error: %@", error); - } else { - NSLog(@"%@ %@", response, responseObject); - } -}]; -[dataTask resume]; -``` - ---- - -### Request Serialization - -Request serializers create requests from URL strings, encoding parameters as either a query string or HTTP body. - -```objective-c -NSString *URLString = @"http://example.com/"; -NSDictionary *parameters = @{@"foo": @"bar", @"baz": @[@1, @2, @3]}; -``` - -#### Query String Parameter Encoding - -```objective-c -[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:URLString parameters:parameters error:nil]; -``` - - GET http://example.com?foo=bar&baz[]=1&baz[]=2&baz[]=3 - -#### URL Form Parameter Encoding - -```objective-c -[[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters error:nil]; -``` - - POST http://example.com/ - Content-Type: application/x-www-form-urlencoded - - foo=bar&baz[]=1&baz[]=2&baz[]=3 - -#### JSON Parameter Encoding - -```objective-c -[[AFJSONRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters error:nil]; -``` - - POST http://example.com/ - Content-Type: application/json - - {"foo": "bar", "baz": [1,2,3]} - ---- - -### Network Reachability Manager - -`AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. - -* Do not use Reachability to determine if the original request should be sent. - * You should try to send it. -* You can use Reachability to determine when a request should be automatically retried. - * Although it may still fail, a Reachability notification that the connectivity is available is a good time to retry something. -* Network reachability is a useful tool for determining why a request might have failed. - * After a network request has failed, telling the user they're offline is better than giving them a more technical but accurate error, such as "request timed out." - -See also [WWDC 2012 session 706, "Networking Best Practices."](https://developer.apple.com/videos/play/wwdc2012-706/). - -#### Shared Network Reachability - -```objective-c -[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { - NSLog(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status)); -}]; - -[[AFNetworkReachabilityManager sharedManager] startMonitoring]; -``` - ---- - -### Security Policy - -`AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. - -Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. - -#### Allowing Invalid SSL Certificates - -```objective-c -AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; -manager.securityPolicy.allowInvalidCertificates = YES; // not recommended for production -``` - ---- - -## Unit Tests - -AFNetworking includes a suite of unit tests within the Tests subdirectory. These tests can be run simply be executed the test action on the platform framework you would like to test. - -## Credits - -AFNetworking is owned and maintained by the [Alamofire Software Foundation](http://alamofire.org). - -AFNetworking was originally created by [Scott Raymond](https://github.com/sco/) and [Mattt Thompson](https://github.com/mattt/) in the development of [Gowalla for iPhone](http://en.wikipedia.org/wiki/Gowalla). - -AFNetworking's logo was designed by [Alan Defibaugh](http://www.alandefibaugh.com/). - -And most of all, thanks to AFNetworking's [growing list of contributors](https://github.com/AFNetworking/AFNetworking/contributors). - -### Security Disclosure - -If you believe you have identified a security vulnerability with AFNetworking, you should report it as soon as possible via email to security@alamofire.org. Please do not post it to a public issue tracker. - -## License - -AFNetworking is released under the MIT license. See LICENSE for details. diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h b/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h deleted file mode 100644 index 9bdc15c..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h +++ /dev/null @@ -1,149 +0,0 @@ -// AFAutoPurgingImageCache.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import -#import - -#if TARGET_OS_IOS || TARGET_OS_TV -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - The `AFImageCache` protocol defines a set of APIs for adding, removing and fetching images from a cache synchronously. - */ -@protocol AFImageCache - -/** - Adds the image to the cache with the given identifier. - - @param image The image to cache. - @param identifier The unique identifier for the image in the cache. - */ -- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier; - -/** - Removes the image from the cache matching the given identifier. - - @param identifier The unique identifier for the image in the cache. - - @return A BOOL indicating whether or not the image was removed from the cache. - */ -- (BOOL)removeImageWithIdentifier:(NSString *)identifier; - -/** - Removes all images from the cache. - - @return A BOOL indicating whether or not all images were removed from the cache. - */ -- (BOOL)removeAllImages; - -/** - Returns the image in the cache associated with the given identifier. - - @param identifier The unique identifier for the image in the cache. - - @return An image for the matching identifier, or nil. - */ -- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier; -@end - - -/** - The `ImageRequestCache` protocol extends the `ImageCache` protocol by adding methods for adding, removing and fetching images from a cache given an `NSURLRequest` and additional identifier. - */ -@protocol AFImageRequestCache - -/** - Adds the image to the cache using an identifier created from the request and additional identifier. - - @param image The image to cache. - @param request The unique URL request identifing the image asset. - @param identifier The additional identifier to apply to the URL request to identify the image. - */ -- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; - -/** - Removes the image from the cache using an identifier created from the request and additional identifier. - - @param request The unique URL request identifing the image asset. - @param identifier The additional identifier to apply to the URL request to identify the image. - - @return A BOOL indicating whether or not all images were removed from the cache. - */ -- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; - -/** - Returns the image from the cache associated with an identifier created from the request and additional identifier. - - @param request The unique URL request identifing the image asset. - @param identifier The additional identifier to apply to the URL request to identify the image. - - @return An image for the matching request and identifier, or nil. - */ -- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; - -@end - -/** - The `AutoPurgingImageCache` in an in-memory image cache used to store images up to a given memory capacity. When the memory capacity is reached, the image cache is sorted by last access date, then the oldest image is continuously purged until the preferred memory usage after purge is met. Each time an image is accessed through the cache, the internal access date of the image is updated. - */ -@interface AFAutoPurgingImageCache : NSObject - -/** - The total memory capacity of the cache in bytes. - */ -@property (nonatomic, assign) UInt64 memoryCapacity; - -/** - The preferred memory usage after purge in bytes. During a purge, images will be purged until the memory capacity drops below this limit. - */ -@property (nonatomic, assign) UInt64 preferredMemoryUsageAfterPurge; - -/** - The current total memory usage in bytes of all images stored within the cache. - */ -@property (nonatomic, assign, readonly) UInt64 memoryUsage; - -/** - Initialies the `AutoPurgingImageCache` instance with default values for memory capacity and preferred memory usage after purge limit. `memoryCapcity` defaults to `100 MB`. `preferredMemoryUsageAfterPurge` defaults to `60 MB`. - - @return The new `AutoPurgingImageCache` instance. - */ -- (instancetype)init; - -/** - Initialies the `AutoPurgingImageCache` instance with the given memory capacity and preferred memory usage - after purge limit. - - @param memoryCapacity The total memory capacity of the cache in bytes. - @param preferredMemoryCapacity The preferred memory usage after purge in bytes. - - @return The new `AutoPurgingImageCache` instance. - */ -- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity; - -@end - -NS_ASSUME_NONNULL_END - -#endif - diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m b/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m deleted file mode 100644 index 1f40715..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m +++ /dev/null @@ -1,201 +0,0 @@ -// AFAutoPurgingImageCache.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import "AFAutoPurgingImageCache.h" - -@interface AFCachedImage : NSObject - -@property (nonatomic, strong) UIImage *image; -@property (nonatomic, strong) NSString *identifier; -@property (nonatomic, assign) UInt64 totalBytes; -@property (nonatomic, strong) NSDate *lastAccessDate; -@property (nonatomic, assign) UInt64 currentMemoryUsage; - -@end - -@implementation AFCachedImage - --(instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier { - if (self = [self init]) { - self.image = image; - self.identifier = identifier; - - CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale); - CGFloat bytesPerPixel = 4.0; - CGFloat bytesPerSize = imageSize.width * imageSize.height; - self.totalBytes = (UInt64)bytesPerPixel * (UInt64)bytesPerSize; - self.lastAccessDate = [NSDate date]; - } - return self; -} - -- (UIImage*)accessImage { - self.lastAccessDate = [NSDate date]; - return self.image; -} - -- (NSString *)description { - NSString *descriptionString = [NSString stringWithFormat:@"Idenfitier: %@ lastAccessDate: %@ ", self.identifier, self.lastAccessDate]; - return descriptionString; - -} - -@end - -@interface AFAutoPurgingImageCache () -@property (nonatomic, strong) NSMutableDictionary *cachedImages; -@property (nonatomic, assign) UInt64 currentMemoryUsage; -@property (nonatomic, strong) dispatch_queue_t synchronizationQueue; -@end - -@implementation AFAutoPurgingImageCache - -- (instancetype)init { - return [self initWithMemoryCapacity:100 * 1024 * 1024 preferredMemoryCapacity:60 * 1024 * 1024]; -} - -- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity { - if (self = [super init]) { - self.memoryCapacity = memoryCapacity; - self.preferredMemoryUsageAfterPurge = preferredMemoryCapacity; - self.cachedImages = [[NSMutableDictionary alloc] init]; - - NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]]; - self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT); - - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(removeAllImages) - name:UIApplicationDidReceiveMemoryWarningNotification - object:nil]; - - } - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (UInt64)memoryUsage { - __block UInt64 result = 0; - dispatch_sync(self.synchronizationQueue, ^{ - result = self.currentMemoryUsage; - }); - return result; -} - -- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier { - dispatch_barrier_async(self.synchronizationQueue, ^{ - AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier]; - - AFCachedImage *previousCachedImage = self.cachedImages[identifier]; - if (previousCachedImage != nil) { - self.currentMemoryUsage -= previousCachedImage.totalBytes; - } - - self.cachedImages[identifier] = cacheImage; - self.currentMemoryUsage += cacheImage.totalBytes; - }); - - dispatch_barrier_async(self.synchronizationQueue, ^{ - if (self.currentMemoryUsage > self.memoryCapacity) { - UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge; - NSMutableArray *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues]; - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate" - ascending:YES]; - [sortedImages sortUsingDescriptors:@[sortDescriptor]]; - - UInt64 bytesPurged = 0; - - for (AFCachedImage *cachedImage in sortedImages) { - [self.cachedImages removeObjectForKey:cachedImage.identifier]; - bytesPurged += cachedImage.totalBytes; - if (bytesPurged >= bytesToPurge) { - break ; - } - } - self.currentMemoryUsage -= bytesPurged; - } - }); -} - -- (BOOL)removeImageWithIdentifier:(NSString *)identifier { - __block BOOL removed = NO; - dispatch_barrier_sync(self.synchronizationQueue, ^{ - AFCachedImage *cachedImage = self.cachedImages[identifier]; - if (cachedImage != nil) { - [self.cachedImages removeObjectForKey:identifier]; - self.currentMemoryUsage -= cachedImage.totalBytes; - removed = YES; - } - }); - return removed; -} - -- (BOOL)removeAllImages { - __block BOOL removed = NO; - dispatch_barrier_sync(self.synchronizationQueue, ^{ - if (self.cachedImages.count > 0) { - [self.cachedImages removeAllObjects]; - self.currentMemoryUsage = 0; - removed = YES; - } - }); - return removed; -} - -- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier { - __block UIImage *image = nil; - dispatch_sync(self.synchronizationQueue, ^{ - AFCachedImage *cachedImage = self.cachedImages[identifier]; - image = [cachedImage accessImage]; - }); - return image; -} - -- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { - [self addImage:image withIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; -} - -- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { - return [self removeImageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; -} - -- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { - return [self imageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; -} - -- (NSString *)imageCacheKeyFromURLRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)additionalIdentifier { - NSString *key = request.URL.absoluteString; - if (additionalIdentifier != nil) { - key = [key stringByAppendingString:additionalIdentifier]; - } - return key; -} - -@end - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h b/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h deleted file mode 100644 index 3903eec..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h +++ /dev/null @@ -1,157 +0,0 @@ -// AFImageDownloader.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import -#import "AFAutoPurgingImageCache.h" -#import "AFHTTPSessionManager.h" - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) { - AFImageDownloadPrioritizationFIFO, - AFImageDownloadPrioritizationLIFO -}; - -/** - The `AFImageDownloadReceipt` is an object vended by the `AFImageDownloader` when starting a data task. It can be used to cancel active tasks running on the `AFImageDownloader` session. As a general rule, image data tasks should be cancelled using the `AFImageDownloadReceipt` instead of calling `cancel` directly on the `task` itself. The `AFImageDownloader` is optimized to handle duplicate task scenarios as well as pending versus active downloads. - */ -@interface AFImageDownloadReceipt : NSObject - -/** - The data task created by the `AFImageDownloader`. -*/ -@property (nonatomic, strong) NSURLSessionDataTask *task; - -/** - The unique identifier for the success and failure blocks when duplicate requests are made. - */ -@property (nonatomic, strong) NSUUID *receiptID; -@end - -/** The `AFImageDownloader` class is responsible for downloading images in parallel on a prioritized queue. Incoming downloads are added to the front or back of the queue depending on the download prioritization. Each downloaded image is cached in the underlying `NSURLCache` as well as the in-memory image cache. By default, any download request with a cached image equivalent in the image cache will automatically be served the cached image representation. - */ -@interface AFImageDownloader : NSObject - -/** - The image cache used to store all downloaded images in. `AFAutoPurgingImageCache` by default. - */ -@property (nonatomic, strong, nullable) id imageCache; - -/** - The `AFHTTPSessionManager` used to download images. By default, this is configured with an `AFImageResponseSerializer`, and a shared `NSURLCache` for all image downloads. - */ -@property (nonatomic, strong) AFHTTPSessionManager *sessionManager; - -/** - Defines the order prioritization of incoming download requests being inserted into the queue. `AFImageDownloadPrioritizationFIFO` by default. - */ -@property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritizaton; - -/** - The shared default instance of `AFImageDownloader` initialized with default values. - */ -+ (instancetype)defaultInstance; - -/** - Creates a default `NSURLCache` with common usage parameter values. - - @returns The default `NSURLCache` instance. - */ -+ (NSURLCache *)defaultURLCache; - -/** - Default initializer - - @return An instance of `AFImageDownloader` initialized with default values. - */ -- (instancetype)init; - -/** - Initializes the `AFImageDownloader` instance with the given session manager, download prioritization, maximum active download count and image cache. - - @param sessionManager The session manager to use to download images. - @param downloadPrioritization The download prioritization of the download queue. - @param maximumActiveDownloads The maximum number of active downloads allowed at any given time. Recommend `4`. - @param imageCache The image cache used to store all downloaded images in. - - @return The new `AFImageDownloader` instance. - */ -- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager - downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization - maximumActiveDownloads:(NSInteger)maximumActiveDownloads - imageCache:(nullable id )imageCache; - -/** - Creates a data task using the `sessionManager` instance for the specified URL request. - - If the same data task is already in the queue or currently being downloaded, the success and failure blocks are - appended to the already existing task. Once the task completes, all success or failure blocks attached to the - task are executed in the order they were added. - - @param request The URL request. - @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. - @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. - - @return The image download receipt for the data task if available. `nil` if the image is stored in the cache. - cache and the URL request cache policy allows the cache to be used. - */ -- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request - success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success - failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; - -/** - Creates a data task using the `sessionManager` instance for the specified URL request. - - If the same data task is already in the queue or currently being downloaded, the success and failure blocks are - appended to the already existing task. Once the task completes, all success or failure blocks attached to the - task are executed in the order they were added. - - @param request The URL request. - @param receiptID The identifier to use for the download receipt that will be created for this request. This must be a unique identifier that does not represent any other request. - @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. - @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. - - @return The image download receipt for the data task if available. `nil` if the image is stored in the cache. - cache and the URL request cache policy allows the cache to be used. - */ -- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request - withReceiptID:(NSUUID *)receiptID - success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success - failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; - -/** - Cancels the data task in the receipt by removing the corresponding success and failure blocks and cancelling the data task if necessary. - - If the data task is pending in the queue, it will be cancelled if no other success and failure blocks are registered with the data task. If the data task is currently executing or is already completed, the success and failure blocks are removed and will not be called when the task finishes. - - @param imageDownloadReceipt The image download receipt to cancel. - */ -- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt; - -@end - -#endif - -NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m b/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m deleted file mode 100644 index 78477bf..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m +++ /dev/null @@ -1,391 +0,0 @@ -// AFImageDownloader.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import "AFImageDownloader.h" -#import "AFHTTPSessionManager.h" - -@interface AFImageDownloaderResponseHandler : NSObject -@property (nonatomic, strong) NSUUID *uuid; -@property (nonatomic, copy) void (^successBlock)(NSURLRequest*, NSHTTPURLResponse*, UIImage*); -@property (nonatomic, copy) void (^failureBlock)(NSURLRequest*, NSHTTPURLResponse*, NSError*); -@end - -@implementation AFImageDownloaderResponseHandler - -- (instancetype)initWithUUID:(NSUUID *)uuid - success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success - failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure { - if (self = [self init]) { - self.uuid = uuid; - self.successBlock = success; - self.failureBlock = failure; - } - return self; -} - -- (NSString *)description { - return [NSString stringWithFormat: @"UUID: %@", [self.uuid UUIDString]]; -} - -@end - -@interface AFImageDownloaderMergedTask : NSObject -@property (nonatomic, strong) NSString *URLIdentifier; -@property (nonatomic, strong) NSUUID *identifier; -@property (nonatomic, strong) NSURLSessionDataTask *task; -@property (nonatomic, strong) NSMutableArray *responseHandlers; - -@end - -@implementation AFImageDownloaderMergedTask - -- (instancetype)initWithURLIdentifier:(NSString *)URLIdentifier identifier:(NSUUID *)identifier task:(NSURLSessionDataTask *)task { - if (self = [self init]) { - self.URLIdentifier = URLIdentifier; - self.task = task; - self.identifier = identifier; - self.responseHandlers = [[NSMutableArray alloc] init]; - } - return self; -} - -- (void)addResponseHandler:(AFImageDownloaderResponseHandler*)handler { - [self.responseHandlers addObject:handler]; -} - -- (void)removeResponseHandler:(AFImageDownloaderResponseHandler*)handler { - [self.responseHandlers removeObject:handler]; -} - -@end - -@implementation AFImageDownloadReceipt - -- (instancetype)initWithReceiptID:(NSUUID *)receiptID task:(NSURLSessionDataTask *)task { - if (self = [self init]) { - self.receiptID = receiptID; - self.task = task; - } - return self; -} - -@end - -@interface AFImageDownloader () - -@property (nonatomic, strong) dispatch_queue_t synchronizationQueue; -@property (nonatomic, strong) dispatch_queue_t responseQueue; - -@property (nonatomic, assign) NSInteger maximumActiveDownloads; -@property (nonatomic, assign) NSInteger activeRequestCount; - -@property (nonatomic, strong) NSMutableArray *queuedMergedTasks; -@property (nonatomic, strong) NSMutableDictionary *mergedTasks; - -@end - - -@implementation AFImageDownloader - -+ (NSURLCache *)defaultURLCache { - return [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024 - diskCapacity:150 * 1024 * 1024 - diskPath:@"com.alamofire.imagedownloader"]; -} - -+ (NSURLSessionConfiguration *)defaultURLSessionConfiguration { - NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; - - //TODO set the default HTTP headers - - configuration.HTTPShouldSetCookies = YES; - configuration.HTTPShouldUsePipelining = NO; - - configuration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy; - configuration.allowsCellularAccess = YES; - configuration.timeoutIntervalForRequest = 60.0; - configuration.URLCache = [AFImageDownloader defaultURLCache]; - - return configuration; -} - -- (instancetype)init { - NSURLSessionConfiguration *defaultConfiguration = [self.class defaultURLSessionConfiguration]; - AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:defaultConfiguration]; - sessionManager.responseSerializer = [AFImageResponseSerializer serializer]; - - return [self initWithSessionManager:sessionManager - downloadPrioritization:AFImageDownloadPrioritizationFIFO - maximumActiveDownloads:4 - imageCache:[[AFAutoPurgingImageCache alloc] init]]; -} - -- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager - downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization - maximumActiveDownloads:(NSInteger)maximumActiveDownloads - imageCache:(id )imageCache { - if (self = [super init]) { - self.sessionManager = sessionManager; - - self.downloadPrioritizaton = downloadPrioritization; - self.maximumActiveDownloads = maximumActiveDownloads; - self.imageCache = imageCache; - - self.queuedMergedTasks = [[NSMutableArray alloc] init]; - self.mergedTasks = [[NSMutableDictionary alloc] init]; - self.activeRequestCount = 0; - - NSString *name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.synchronizationqueue-%@", [[NSUUID UUID] UUIDString]]; - self.synchronizationQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL); - - name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.responsequeue-%@", [[NSUUID UUID] UUIDString]]; - self.responseQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT); - } - - return self; -} - -+ (instancetype)defaultInstance { - static AFImageDownloader *sharedInstance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [[self alloc] init]; - }); - return sharedInstance; -} - -- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request - success:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, UIImage * _Nonnull))success - failure:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, NSError * _Nonnull))failure { - return [self downloadImageForURLRequest:request withReceiptID:[NSUUID UUID] success:success failure:failure]; -} - -- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request - withReceiptID:(nonnull NSUUID *)receiptID - success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success - failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure { - __block NSURLSessionDataTask *task = nil; - dispatch_sync(self.synchronizationQueue, ^{ - NSString *URLIdentifier = request.URL.absoluteString; - if (URLIdentifier == nil) { - if (failure) { - NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil]; - dispatch_async(dispatch_get_main_queue(), ^{ - failure(request, nil, error); - }); - } - return; - } - - // 1) Append the success and failure blocks to a pre-existing request if it already exists - AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[URLIdentifier]; - if (existingMergedTask != nil) { - AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure]; - [existingMergedTask addResponseHandler:handler]; - task = existingMergedTask.task; - return; - } - - // 2) Attempt to load the image from the image cache if the cache policy allows it - switch (request.cachePolicy) { - case NSURLRequestUseProtocolCachePolicy: - case NSURLRequestReturnCacheDataElseLoad: - case NSURLRequestReturnCacheDataDontLoad: { - UIImage *cachedImage = [self.imageCache imageforRequest:request withAdditionalIdentifier:nil]; - if (cachedImage != nil) { - if (success) { - dispatch_async(dispatch_get_main_queue(), ^{ - success(request, nil, cachedImage); - }); - } - return; - } - break; - } - default: - break; - } - - // 3) Create the request and set up authentication, validation and response serialization - NSUUID *mergedTaskIdentifier = [NSUUID UUID]; - NSURLSessionDataTask *createdTask; - __weak __typeof__(self) weakSelf = self; - - createdTask = [self.sessionManager - dataTaskWithRequest:request - completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { - dispatch_async(self.responseQueue, ^{ - __strong __typeof__(weakSelf) strongSelf = weakSelf; - AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier]; - if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) { - mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier]; - if (error) { - for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) { - if (handler.failureBlock) { - dispatch_async(dispatch_get_main_queue(), ^{ - handler.failureBlock(request, (NSHTTPURLResponse*)response, error); - }); - } - } - } else { - [strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil]; - - for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) { - if (handler.successBlock) { - dispatch_async(dispatch_get_main_queue(), ^{ - handler.successBlock(request, (NSHTTPURLResponse*)response, responseObject); - }); - } - } - - } - } - [strongSelf safelyDecrementActiveTaskCount]; - [strongSelf safelyStartNextTaskIfNecessary]; - }); - }]; - - // 4) Store the response handler for use when the request completes - AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID - success:success - failure:failure]; - AFImageDownloaderMergedTask *mergedTask = [[AFImageDownloaderMergedTask alloc] - initWithURLIdentifier:URLIdentifier - identifier:mergedTaskIdentifier - task:createdTask]; - [mergedTask addResponseHandler:handler]; - self.mergedTasks[URLIdentifier] = mergedTask; - - // 5) Either start the request or enqueue it depending on the current active request count - if ([self isActiveRequestCountBelowMaximumLimit]) { - [self startMergedTask:mergedTask]; - } else { - [self enqueueMergedTask:mergedTask]; - } - - task = mergedTask.task; - }); - if (task) { - return [[AFImageDownloadReceipt alloc] initWithReceiptID:receiptID task:task]; - } else { - return nil; - } -} - -- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt { - dispatch_sync(self.synchronizationQueue, ^{ - NSString *URLIdentifier = imageDownloadReceipt.task.originalRequest.URL.absoluteString; - AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier]; - NSUInteger index = [mergedTask.responseHandlers indexOfObjectPassingTest:^BOOL(AFImageDownloaderResponseHandler * _Nonnull handler, __unused NSUInteger idx, __unused BOOL * _Nonnull stop) { - return handler.uuid == imageDownloadReceipt.receiptID; - }]; - - if (index != NSNotFound) { - AFImageDownloaderResponseHandler *handler = mergedTask.responseHandlers[index]; - [mergedTask removeResponseHandler:handler]; - NSString *failureReason = [NSString stringWithFormat:@"ImageDownloader cancelled URL request: %@",imageDownloadReceipt.task.originalRequest.URL.absoluteString]; - NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey:failureReason}; - NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo]; - if (handler.failureBlock) { - dispatch_async(dispatch_get_main_queue(), ^{ - handler.failureBlock(imageDownloadReceipt.task.originalRequest, nil, error); - }); - } - } - - if (mergedTask.responseHandlers.count == 0 && mergedTask.task.state == NSURLSessionTaskStateSuspended) { - [mergedTask.task cancel]; - [self removeMergedTaskWithURLIdentifier:URLIdentifier]; - } - }); -} - -- (AFImageDownloaderMergedTask*)safelyRemoveMergedTaskWithURLIdentifier:(NSString *)URLIdentifier { - __block AFImageDownloaderMergedTask *mergedTask = nil; - dispatch_sync(self.synchronizationQueue, ^{ - mergedTask = [self removeMergedTaskWithURLIdentifier:URLIdentifier]; - }); - return mergedTask; -} - -//This method should only be called from safely within the synchronizationQueue -- (AFImageDownloaderMergedTask *)removeMergedTaskWithURLIdentifier:(NSString *)URLIdentifier { - AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier]; - [self.mergedTasks removeObjectForKey:URLIdentifier]; - return mergedTask; -} - -- (void)safelyDecrementActiveTaskCount { - dispatch_sync(self.synchronizationQueue, ^{ - if (self.activeRequestCount > 0) { - self.activeRequestCount -= 1; - } - }); -} - -- (void)safelyStartNextTaskIfNecessary { - dispatch_sync(self.synchronizationQueue, ^{ - if ([self isActiveRequestCountBelowMaximumLimit]) { - while (self.queuedMergedTasks.count > 0) { - AFImageDownloaderMergedTask *mergedTask = [self dequeueMergedTask]; - if (mergedTask.task.state == NSURLSessionTaskStateSuspended) { - [self startMergedTask:mergedTask]; - break; - } - } - } - }); -} - -- (void)startMergedTask:(AFImageDownloaderMergedTask *)mergedTask { - [mergedTask.task resume]; - ++self.activeRequestCount; -} - -- (void)enqueueMergedTask:(AFImageDownloaderMergedTask *)mergedTask { - switch (self.downloadPrioritizaton) { - case AFImageDownloadPrioritizationFIFO: - [self.queuedMergedTasks addObject:mergedTask]; - break; - case AFImageDownloadPrioritizationLIFO: - [self.queuedMergedTasks insertObject:mergedTask atIndex:0]; - break; - } -} - -- (AFImageDownloaderMergedTask *)dequeueMergedTask { - AFImageDownloaderMergedTask *mergedTask = nil; - mergedTask = [self.queuedMergedTasks firstObject]; - [self.queuedMergedTasks removeObject:mergedTask]; - return mergedTask; -} - -- (BOOL)isActiveRequestCountBelowMaximumLimit { - return self.activeRequestCount < self.maximumActiveDownloads; -} - -@end - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h deleted file mode 100644 index 3bcf289..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h +++ /dev/null @@ -1,103 +0,0 @@ -// AFNetworkActivityIndicatorManager.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#import - -#if TARGET_OS_IOS - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - `AFNetworkActivityIndicatorManager` manages the state of the network activity indicator in the status bar. When enabled, it will listen for notifications indicating that a session task has started or finished, and start or stop animating the indicator accordingly. The number of active requests is incremented and decremented much like a stack or a semaphore, and the activity indicator will animate so long as that number is greater than zero. - - You should enable the shared instance of `AFNetworkActivityIndicatorManager` when your application finishes launching. In `AppDelegate application:didFinishLaunchingWithOptions:` you can do so with the following code: - - [[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES]; - - By setting `enabled` to `YES` for `sharedManager`, the network activity indicator will show and hide automatically as requests start and finish. You should not ever need to call `incrementActivityCount` or `decrementActivityCount` yourself. - - See the Apple Human Interface Guidelines section about the Network Activity Indicator for more information: - http://developer.apple.com/library/iOS/#documentation/UserExperience/Conceptual/MobileHIG/UIElementGuidelines/UIElementGuidelines.html#//apple_ref/doc/uid/TP40006556-CH13-SW44 - */ -NS_EXTENSION_UNAVAILABLE_IOS("Use view controller based solutions where appropriate instead.") -@interface AFNetworkActivityIndicatorManager : NSObject - -/** - A Boolean value indicating whether the manager is enabled. - - If YES, the manager will change status bar network activity indicator according to network operation notifications it receives. The default value is NO. - */ -@property (nonatomic, assign, getter = isEnabled) BOOL enabled; - -/** - A Boolean value indicating whether the network activity indicator manager is currently active. -*/ -@property (readonly, nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; - -/** - A time interval indicating the minimum duration of networking activity that should occur before the activity indicator is displayed. The default value 1 second. If the network activity indicator should be displayed immediately when network activity occurs, this value should be set to 0 seconds. - - Apple's HIG describes the following: - - > Display the network activity indicator to provide feedback when your app accesses the network for more than a couple of seconds. If the operation finishes sooner than that, you don’t have to show the network activity indicator, because the indicator is likely to disappear before users notice its presence. - - */ -@property (nonatomic, assign) NSTimeInterval activationDelay; - -/** - A time interval indicating the duration of time of no networking activity required before the activity indicator is disabled. This allows for continuous display of the network activity indicator across multiple requests. The default value is 0.17 seconds. - */ - -@property (nonatomic, assign) NSTimeInterval completionDelay; - -/** - Returns the shared network activity indicator manager object for the system. - - @return The systemwide network activity indicator manager. - */ -+ (instancetype)sharedManager; - -/** - Increments the number of active network requests. If this number was zero before incrementing, this will start animating the status bar network activity indicator. - */ -- (void)incrementActivityCount; - -/** - Decrements the number of active network requests. If this number becomes zero after decrementing, this will stop animating the status bar network activity indicator. - */ -- (void)decrementActivityCount; - -/** - Set the a custom method to be executed when the network activity indicator manager should be hidden/shown. By default, this is null, and the UIApplication Network Activity Indicator will be managed automatically. If this block is set, it is the responsiblity of the caller to manager the network activity indicator going forward. - - @param block A block to be executed when the network activity indicator status changes. - */ -- (void)setNetworkingActivityActionWithBlock:(nullable void (^)(BOOL networkActivityIndicatorVisible))block; - -@end - -NS_ASSUME_NONNULL_END - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m deleted file mode 100644 index e77508e..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m +++ /dev/null @@ -1,261 +0,0 @@ -// AFNetworkActivityIndicatorManager.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "AFNetworkActivityIndicatorManager.h" - -#if TARGET_OS_IOS -#import "AFURLSessionManager.h" - -typedef NS_ENUM(NSInteger, AFNetworkActivityManagerState) { - AFNetworkActivityManagerStateNotActive, - AFNetworkActivityManagerStateDelayingStart, - AFNetworkActivityManagerStateActive, - AFNetworkActivityManagerStateDelayingEnd -}; - -static NSTimeInterval const kDefaultAFNetworkActivityManagerActivationDelay = 1.0; -static NSTimeInterval const kDefaultAFNetworkActivityManagerCompletionDelay = 0.17; - -static NSURLRequest * AFNetworkRequestFromNotification(NSNotification *notification) { - if ([[notification object] respondsToSelector:@selector(originalRequest)]) { - return [(NSURLSessionTask *)[notification object] originalRequest]; - } else { - return nil; - } -} - -typedef void (^AFNetworkActivityActionBlock)(BOOL networkActivityIndicatorVisible); - -@interface AFNetworkActivityIndicatorManager () -@property (readwrite, nonatomic, assign) NSInteger activityCount; -@property (readwrite, nonatomic, strong) NSTimer *activationDelayTimer; -@property (readwrite, nonatomic, strong) NSTimer *completionDelayTimer; -@property (readonly, nonatomic, getter = isNetworkActivityOccurring) BOOL networkActivityOccurring; -@property (nonatomic, copy) AFNetworkActivityActionBlock networkActivityActionBlock; -@property (nonatomic, assign) AFNetworkActivityManagerState currentState; -@property (nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; - -- (void)updateCurrentStateForNetworkActivityChange; -@end - -@implementation AFNetworkActivityIndicatorManager - -+ (instancetype)sharedManager { - static AFNetworkActivityIndicatorManager *_sharedManager = nil; - static dispatch_once_t oncePredicate; - dispatch_once(&oncePredicate, ^{ - _sharedManager = [[self alloc] init]; - }); - - return _sharedManager; -} - -- (instancetype)init { - self = [super init]; - if (!self) { - return nil; - } - self.currentState = AFNetworkActivityManagerStateNotActive; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingTaskDidResumeNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidSuspendNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidCompleteNotification object:nil]; - self.activationDelay = kDefaultAFNetworkActivityManagerActivationDelay; - self.completionDelay = kDefaultAFNetworkActivityManagerCompletionDelay; - - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [_activationDelayTimer invalidate]; - [_completionDelayTimer invalidate]; -} - -- (void)setEnabled:(BOOL)enabled { - _enabled = enabled; - if (enabled == NO) { - [self setCurrentState:AFNetworkActivityManagerStateNotActive]; - } -} - -- (void)setNetworkingActivityActionWithBlock:(void (^)(BOOL networkActivityIndicatorVisible))block { - self.networkActivityActionBlock = block; -} - -- (BOOL)isNetworkActivityOccurring { - @synchronized(self) { - return self.activityCount > 0; - } -} - -- (void)setNetworkActivityIndicatorVisible:(BOOL)networkActivityIndicatorVisible { - if (_networkActivityIndicatorVisible != networkActivityIndicatorVisible) { - [self willChangeValueForKey:@"networkActivityIndicatorVisible"]; - @synchronized(self) { - _networkActivityIndicatorVisible = networkActivityIndicatorVisible; - } - [self didChangeValueForKey:@"networkActivityIndicatorVisible"]; - if (self.networkActivityActionBlock) { - self.networkActivityActionBlock(networkActivityIndicatorVisible); - } else { - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:networkActivityIndicatorVisible]; - } - } -} - -- (void)setActivityCount:(NSInteger)activityCount { - @synchronized(self) { - _activityCount = activityCount; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - [self updateCurrentStateForNetworkActivityChange]; - }); -} - -- (void)incrementActivityCount { - [self willChangeValueForKey:@"activityCount"]; - @synchronized(self) { - _activityCount++; - } - [self didChangeValueForKey:@"activityCount"]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [self updateCurrentStateForNetworkActivityChange]; - }); -} - -- (void)decrementActivityCount { - [self willChangeValueForKey:@"activityCount"]; - @synchronized(self) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" - _activityCount = MAX(_activityCount - 1, 0); -#pragma clang diagnostic pop - } - [self didChangeValueForKey:@"activityCount"]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [self updateCurrentStateForNetworkActivityChange]; - }); -} - -- (void)networkRequestDidStart:(NSNotification *)notification { - if ([AFNetworkRequestFromNotification(notification) URL]) { - [self incrementActivityCount]; - } -} - -- (void)networkRequestDidFinish:(NSNotification *)notification { - if ([AFNetworkRequestFromNotification(notification) URL]) { - [self decrementActivityCount]; - } -} - -#pragma mark - Internal State Management -- (void)setCurrentState:(AFNetworkActivityManagerState)currentState { - @synchronized(self) { - if (_currentState != currentState) { - [self willChangeValueForKey:@"currentState"]; - _currentState = currentState; - switch (currentState) { - case AFNetworkActivityManagerStateNotActive: - [self cancelActivationDelayTimer]; - [self cancelCompletionDelayTimer]; - [self setNetworkActivityIndicatorVisible:NO]; - break; - case AFNetworkActivityManagerStateDelayingStart: - [self startActivationDelayTimer]; - break; - case AFNetworkActivityManagerStateActive: - [self cancelCompletionDelayTimer]; - [self setNetworkActivityIndicatorVisible:YES]; - break; - case AFNetworkActivityManagerStateDelayingEnd: - [self startCompletionDelayTimer]; - break; - } - } - [self didChangeValueForKey:@"currentState"]; - } -} - -- (void)updateCurrentStateForNetworkActivityChange { - if (self.enabled) { - switch (self.currentState) { - case AFNetworkActivityManagerStateNotActive: - if (self.isNetworkActivityOccurring) { - [self setCurrentState:AFNetworkActivityManagerStateDelayingStart]; - } - break; - case AFNetworkActivityManagerStateDelayingStart: - //No op. Let the delay timer finish out. - break; - case AFNetworkActivityManagerStateActive: - if (!self.isNetworkActivityOccurring) { - [self setCurrentState:AFNetworkActivityManagerStateDelayingEnd]; - } - break; - case AFNetworkActivityManagerStateDelayingEnd: - if (self.isNetworkActivityOccurring) { - [self setCurrentState:AFNetworkActivityManagerStateActive]; - } - break; - } - } -} - -- (void)startActivationDelayTimer { - self.activationDelayTimer = [NSTimer - timerWithTimeInterval:self.activationDelay target:self selector:@selector(activationDelayTimerFired) userInfo:nil repeats:NO]; - [[NSRunLoop mainRunLoop] addTimer:self.activationDelayTimer forMode:NSRunLoopCommonModes]; -} - -- (void)activationDelayTimerFired { - if (self.networkActivityOccurring) { - [self setCurrentState:AFNetworkActivityManagerStateActive]; - } else { - [self setCurrentState:AFNetworkActivityManagerStateNotActive]; - } -} - -- (void)startCompletionDelayTimer { - [self.completionDelayTimer invalidate]; - self.completionDelayTimer = [NSTimer timerWithTimeInterval:self.completionDelay target:self selector:@selector(completionDelayTimerFired) userInfo:nil repeats:NO]; - [[NSRunLoop mainRunLoop] addTimer:self.completionDelayTimer forMode:NSRunLoopCommonModes]; -} - -- (void)completionDelayTimerFired { - [self setCurrentState:AFNetworkActivityManagerStateNotActive]; -} - -- (void)cancelActivationDelayTimer { - [self.activationDelayTimer invalidate]; -} - -- (void)cancelCompletionDelayTimer { - [self.completionDelayTimer invalidate]; -} - -@end - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h deleted file mode 100644 index d424c9b..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h +++ /dev/null @@ -1,48 +0,0 @@ -// UIActivityIndicatorView+AFNetworking.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import - -/** - This category adds methods to the UIKit framework's `UIActivityIndicatorView` class. The methods in this category provide support for automatically starting and stopping animation depending on the loading state of a session task. - */ -@interface UIActivityIndicatorView (AFNetworking) - -///---------------------------------- -/// @name Animating for Session Tasks -///---------------------------------- - -/** - Binds the animating state to the state of the specified task. - - @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled. - */ -- (void)setAnimatingWithStateOfTask:(nullable NSURLSessionTask *)task; - -@end - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m deleted file mode 100644 index ed704ed..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m +++ /dev/null @@ -1,124 +0,0 @@ -// UIActivityIndicatorView+AFNetworking.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "UIActivityIndicatorView+AFNetworking.h" -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import "AFURLSessionManager.h" - -@interface AFActivityIndicatorViewNotificationObserver : NSObject -@property (readonly, nonatomic, weak) UIActivityIndicatorView *activityIndicatorView; -- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView; - -- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task; - -@end - -@implementation UIActivityIndicatorView (AFNetworking) - -- (AFActivityIndicatorViewNotificationObserver *)af_notificationObserver { - AFActivityIndicatorViewNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver)); - if (notificationObserver == nil) { - notificationObserver = [[AFActivityIndicatorViewNotificationObserver alloc] initWithActivityIndicatorView:self]; - objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - return notificationObserver; -} - -- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task { - [[self af_notificationObserver] setAnimatingWithStateOfTask:task]; -} - -@end - -@implementation AFActivityIndicatorViewNotificationObserver - -- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView -{ - self = [super init]; - if (self) { - _activityIndicatorView = activityIndicatorView; - } - return self; -} - -- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task { - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - - [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; - [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; - [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; - - if (task) { - if (task.state != NSURLSessionTaskStateCompleted) { - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wreceiver-is-weak" -#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak" - if (task.state == NSURLSessionTaskStateRunning) { - [self.activityIndicatorView startAnimating]; - } else { - [self.activityIndicatorView stopAnimating]; - } -#pragma clang diagnostic pop - - [notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingTaskDidResumeNotification object:task]; - [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidCompleteNotification object:task]; - [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidSuspendNotification object:task]; - } - } -} - -#pragma mark - - -- (void)af_startAnimating { - dispatch_async(dispatch_get_main_queue(), ^{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wreceiver-is-weak" - [self.activityIndicatorView startAnimating]; -#pragma clang diagnostic pop - }); -} - -- (void)af_stopAnimating { - dispatch_async(dispatch_get_main_queue(), ^{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wreceiver-is-weak" - [self.activityIndicatorView stopAnimating]; -#pragma clang diagnostic pop - }); -} - -#pragma mark - - -- (void)dealloc { - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - - [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; - [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; - [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; -} - -@end - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h deleted file mode 100644 index d33e0d4..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h +++ /dev/null @@ -1,175 +0,0 @@ -// UIButton+AFNetworking.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class AFImageDownloader; - -/** - This category adds methods to the UIKit framework's `UIButton` class. The methods in this category provide support for loading remote images and background images asynchronously from a URL. - - @warning Compound values for control `state` (such as `UIControlStateHighlighted | UIControlStateDisabled`) are unsupported. - */ -@interface UIButton (AFNetworking) - -///------------------------------------ -/// @name Accessing the Image Downloader -///------------------------------------ - -/** - Set the shared image downloader used to download images. - - @param imageDownloader The shared image downloader used to download images. -*/ -+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader; - -/** - The shared image downloader used to download images. - */ -+ (AFImageDownloader *)sharedImageDownloader; - -///-------------------- -/// @name Setting Image -///-------------------- - -/** - Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. - - If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - - @param state The control state. - @param url The URL used for the image request. - */ -- (void)setImageForState:(UIControlState)state - withURL:(NSURL *)url; - -/** - Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. - - If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - - @param state The control state. - @param url The URL used for the image request. - @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. - */ -- (void)setImageForState:(UIControlState)state - withURL:(NSURL *)url - placeholderImage:(nullable UIImage *)placeholderImage; - -/** - Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. - - If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - - If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setImage:forState:` is applied. - - @param state The control state. - @param urlRequest The URL request used for the image request. - @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. - @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. - @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. - */ -- (void)setImageForState:(UIControlState)state - withURLRequest:(NSURLRequest *)urlRequest - placeholderImage:(nullable UIImage *)placeholderImage - success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success - failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; - - -///------------------------------- -/// @name Setting Background Image -///------------------------------- - -/** - Asynchronously downloads an image from the specified URL, and sets it as the background image for the specified state once the request is finished. Any previous background image request for the receiver will be cancelled. - - If the background image is cached locally, the background image is set immediately, otherwise the specified placeholder background image will be set immediately, and then the remote background image will be set once the request is finished. - - @param state The control state. - @param url The URL used for the background image request. - */ -- (void)setBackgroundImageForState:(UIControlState)state - withURL:(NSURL *)url; - -/** - Asynchronously downloads an image from the specified URL, and sets it as the background image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. - - If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - - @param state The control state. - @param url The URL used for the background image request. - @param placeholderImage The background image to be set initially, until the background image request finishes. If `nil`, the button will not change its background image until the background image request finishes. - */ -- (void)setBackgroundImageForState:(UIControlState)state - withURL:(NSURL *)url - placeholderImage:(nullable UIImage *)placeholderImage; - -/** - Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. - - If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - - If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setBackgroundImage:forState:` is applied. - - @param state The control state. - @param urlRequest The URL request used for the image request. - @param placeholderImage The background image to be set initially, until the background image request finishes. If `nil`, the button will not change its background image until the background image request finishes. - @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. - @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. - */ -- (void)setBackgroundImageForState:(UIControlState)state - withURLRequest:(NSURLRequest *)urlRequest - placeholderImage:(nullable UIImage *)placeholderImage - success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success - failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; - - -///------------------------------ -/// @name Canceling Image Loading -///------------------------------ - -/** - Cancels any executing image task for the specified control state of the receiver, if one exists. - - @param state The control state. - */ -- (void)cancelImageDownloadTaskForState:(UIControlState)state; - -/** - Cancels any executing background image task for the specified control state of the receiver, if one exists. - - @param state The control state. - */ -- (void)cancelBackgroundImageDownloadTaskForState:(UIControlState)state; - -@end - -NS_ASSUME_NONNULL_END - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m deleted file mode 100644 index 5bc49dd..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m +++ /dev/null @@ -1,305 +0,0 @@ -// UIButton+AFNetworking.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "UIButton+AFNetworking.h" - -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import "UIImageView+AFNetworking.h" -#import "AFImageDownloader.h" - -@interface UIButton (_AFNetworking) -@end - -@implementation UIButton (_AFNetworking) - -#pragma mark - - -static char AFImageDownloadReceiptNormal; -static char AFImageDownloadReceiptHighlighted; -static char AFImageDownloadReceiptSelected; -static char AFImageDownloadReceiptDisabled; - -static const char * af_imageDownloadReceiptKeyForState(UIControlState state) { - switch (state) { - case UIControlStateHighlighted: - return &AFImageDownloadReceiptHighlighted; - case UIControlStateSelected: - return &AFImageDownloadReceiptSelected; - case UIControlStateDisabled: - return &AFImageDownloadReceiptDisabled; - case UIControlStateNormal: - default: - return &AFImageDownloadReceiptNormal; - } -} - -- (AFImageDownloadReceipt *)af_imageDownloadReceiptForState:(UIControlState)state { - return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_imageDownloadReceiptKeyForState(state)); -} - -- (void)af_setImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt - forState:(UIControlState)state -{ - objc_setAssociatedObject(self, af_imageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -#pragma mark - - -static char AFBackgroundImageDownloadReceiptNormal; -static char AFBackgroundImageDownloadReceiptHighlighted; -static char AFBackgroundImageDownloadReceiptSelected; -static char AFBackgroundImageDownloadReceiptDisabled; - -static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState state) { - switch (state) { - case UIControlStateHighlighted: - return &AFBackgroundImageDownloadReceiptHighlighted; - case UIControlStateSelected: - return &AFBackgroundImageDownloadReceiptSelected; - case UIControlStateDisabled: - return &AFBackgroundImageDownloadReceiptDisabled; - case UIControlStateNormal: - default: - return &AFBackgroundImageDownloadReceiptNormal; - } -} - -- (AFImageDownloadReceipt *)af_backgroundImageDownloadReceiptForState:(UIControlState)state { - return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state)); -} - -- (void)af_setBackgroundImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt - forState:(UIControlState)state -{ - objc_setAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -@end - -#pragma mark - - -@implementation UIButton (AFNetworking) - -+ (AFImageDownloader *)sharedImageDownloader { - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" - return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance]; -#pragma clang diagnostic pop -} - -+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader { - objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -#pragma mark - - -- (void)setImageForState:(UIControlState)state - withURL:(NSURL *)url -{ - [self setImageForState:state withURL:url placeholderImage:nil]; -} - -- (void)setImageForState:(UIControlState)state - withURL:(NSURL *)url - placeholderImage:(UIImage *)placeholderImage -{ - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; - - [self setImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; -} - -- (void)setImageForState:(UIControlState)state - withURLRequest:(NSURLRequest *)urlRequest - placeholderImage:(nullable UIImage *)placeholderImage - success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success - failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure -{ - if ([self isActiveTaskURLEqualToURLRequest:urlRequest forState:state]) { - return; - } - - [self cancelImageDownloadTaskForState:state]; - - AFImageDownloader *downloader = [[self class] sharedImageDownloader]; - id imageCache = downloader.imageCache; - - //Use the image from the image cache if it exists - UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; - if (cachedImage) { - if (success) { - success(urlRequest, nil, cachedImage); - } else { - [self setImage:cachedImage forState:state]; - } - [self af_setImageDownloadReceipt:nil forState:state]; - } else { - if (placeholderImage) { - [self setImage:placeholderImage forState:state]; - } - - __weak __typeof(self)weakSelf = self; - NSUUID *downloadID = [NSUUID UUID]; - AFImageDownloadReceipt *receipt; - receipt = [downloader - downloadImageForURLRequest:urlRequest - withReceiptID:downloadID - success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { - __strong __typeof(weakSelf)strongSelf = weakSelf; - if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { - if (success) { - success(request, response, responseObject); - } else if(responseObject) { - [strongSelf setImage:responseObject forState:state]; - } - [strongSelf af_setImageDownloadReceipt:nil forState:state]; - } - - } - failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { - __strong __typeof(weakSelf)strongSelf = weakSelf; - if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { - if (failure) { - failure(request, response, error); - } - [strongSelf af_setImageDownloadReceipt:nil forState:state]; - } - }]; - - [self af_setImageDownloadReceipt:receipt forState:state]; - } -} - -#pragma mark - - -- (void)setBackgroundImageForState:(UIControlState)state - withURL:(NSURL *)url -{ - [self setBackgroundImageForState:state withURL:url placeholderImage:nil]; -} - -- (void)setBackgroundImageForState:(UIControlState)state - withURL:(NSURL *)url - placeholderImage:(nullable UIImage *)placeholderImage -{ - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; - - [self setBackgroundImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; -} - -- (void)setBackgroundImageForState:(UIControlState)state - withURLRequest:(NSURLRequest *)urlRequest - placeholderImage:(nullable UIImage *)placeholderImage - success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success - failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure -{ - if ([self isActiveBackgroundTaskURLEqualToURLRequest:urlRequest forState:state]) { - return; - } - - [self cancelBackgroundImageDownloadTaskForState:state]; - - AFImageDownloader *downloader = [[self class] sharedImageDownloader]; - id imageCache = downloader.imageCache; - - //Use the image from the image cache if it exists - UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; - if (cachedImage) { - if (success) { - success(urlRequest, nil, cachedImage); - } else { - [self setBackgroundImage:cachedImage forState:state]; - } - [self af_setBackgroundImageDownloadReceipt:nil forState:state]; - } else { - if (placeholderImage) { - [self setBackgroundImage:placeholderImage forState:state]; - } - - __weak __typeof(self)weakSelf = self; - NSUUID *downloadID = [NSUUID UUID]; - AFImageDownloadReceipt *receipt; - receipt = [downloader - downloadImageForURLRequest:urlRequest - withReceiptID:downloadID - success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { - __strong __typeof(weakSelf)strongSelf = weakSelf; - if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { - if (success) { - success(request, response, responseObject); - } else if(responseObject) { - [strongSelf setBackgroundImage:responseObject forState:state]; - } - [strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state]; - } - - } - failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { - __strong __typeof(weakSelf)strongSelf = weakSelf; - if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { - if (failure) { - failure(request, response, error); - } - [strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state]; - } - }]; - - [self af_setBackgroundImageDownloadReceipt:receipt forState:state]; - } -} - -#pragma mark - - -- (void)cancelImageDownloadTaskForState:(UIControlState)state { - AFImageDownloadReceipt *receipt = [self af_imageDownloadReceiptForState:state]; - if (receipt != nil) { - [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:receipt]; - [self af_setImageDownloadReceipt:nil forState:state]; - } -} - -- (void)cancelBackgroundImageDownloadTaskForState:(UIControlState)state { - AFImageDownloadReceipt *receipt = [self af_backgroundImageDownloadReceiptForState:state]; - if (receipt != nil) { - [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:receipt]; - [self af_setBackgroundImageDownloadReceipt:nil forState:state]; - } -} - -- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest forState:(UIControlState)state { - AFImageDownloadReceipt *receipt = [self af_imageDownloadReceiptForState:state]; - return [receipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString]; -} - -- (BOOL)isActiveBackgroundTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest forState:(UIControlState)state { - AFImageDownloadReceipt *receipt = [self af_backgroundImageDownloadReceiptForState:state]; - return [receipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString]; -} - - -@end - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h deleted file mode 100644 index 14744cd..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// UIImage+AFNetworking.h -// -// -// Created by Paulo Ferreira on 08/07/15. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import - -@interface UIImage (AFNetworking) - -+ (UIImage*) safeImageWithData:(NSData*)data; - -@end - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h deleted file mode 100644 index 8929252..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h +++ /dev/null @@ -1,109 +0,0 @@ -// UIImageView+AFNetworking.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class AFImageDownloader; - -/** - This category adds methods to the UIKit framework's `UIImageView` class. The methods in this category provide support for loading remote images asynchronously from a URL. - */ -@interface UIImageView (AFNetworking) - -///------------------------------------ -/// @name Accessing the Image Downloader -///------------------------------------ - -/** - Set the shared image downloader used to download images. - - @param imageDownloader The shared image downloader used to download images. - */ -+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader; - -/** - The shared image downloader used to download images. - */ -+ (AFImageDownloader *)sharedImageDownloader; - -///-------------------- -/// @name Setting Image -///-------------------- - -/** - Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. - - If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - - By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` - - @param url The URL used for the image request. - */ -- (void)setImageWithURL:(NSURL *)url; - -/** - Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. - - If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - - By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` - - @param url The URL used for the image request. - @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes. - */ -- (void)setImageWithURL:(NSURL *)url - placeholderImage:(nullable UIImage *)placeholderImage; - -/** - Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. - - If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - - If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied. - - @param urlRequest The URL request used for the image request. - @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes. - @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. - @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. - */ -- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest - placeholderImage:(nullable UIImage *)placeholderImage - success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success - failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; - -/** - Cancels any executing image operation for the receiver, if one exists. - */ -- (void)cancelImageDownloadTask; - -@end - -NS_ASSUME_NONNULL_END - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m deleted file mode 100644 index 5934d68..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m +++ /dev/null @@ -1,161 +0,0 @@ -// UIImageView+AFNetworking.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "UIImageView+AFNetworking.h" - -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import "AFImageDownloader.h" - -@interface UIImageView (_AFNetworking) -@property (readwrite, nonatomic, strong, setter = af_setActiveImageDownloadReceipt:) AFImageDownloadReceipt *af_activeImageDownloadReceipt; -@end - -@implementation UIImageView (_AFNetworking) - -- (AFImageDownloadReceipt *)af_activeImageDownloadReceipt { - return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, @selector(af_activeImageDownloadReceipt)); -} - -- (void)af_setActiveImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt { - objc_setAssociatedObject(self, @selector(af_activeImageDownloadReceipt), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -@end - -#pragma mark - - -@implementation UIImageView (AFNetworking) - -+ (AFImageDownloader *)sharedImageDownloader { - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" - return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance]; -#pragma clang diagnostic pop -} - -+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader { - objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -#pragma mark - - -- (void)setImageWithURL:(NSURL *)url { - [self setImageWithURL:url placeholderImage:nil]; -} - -- (void)setImageWithURL:(NSURL *)url - placeholderImage:(UIImage *)placeholderImage -{ - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; - - [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; -} - -- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest - placeholderImage:(UIImage *)placeholderImage - success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success - failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure -{ - - if ([urlRequest URL] == nil) { - [self cancelImageDownloadTask]; - self.image = placeholderImage; - return; - } - - if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){ - return; - } - - [self cancelImageDownloadTask]; - - AFImageDownloader *downloader = [[self class] sharedImageDownloader]; - id imageCache = downloader.imageCache; - - //Use the image from the image cache if it exists - UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; - if (cachedImage) { - if (success) { - success(urlRequest, nil, cachedImage); - } else { - self.image = cachedImage; - } - [self clearActiveDownloadInformation]; - } else { - if (placeholderImage) { - self.image = placeholderImage; - } - - __weak __typeof(self)weakSelf = self; - NSUUID *downloadID = [NSUUID UUID]; - AFImageDownloadReceipt *receipt; - receipt = [downloader - downloadImageForURLRequest:urlRequest - withReceiptID:downloadID - success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { - __strong __typeof(weakSelf)strongSelf = weakSelf; - if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) { - if (success) { - success(request, response, responseObject); - } else if(responseObject) { - strongSelf.image = responseObject; - } - [strongSelf clearActiveDownloadInformation]; - } - - } - failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { - __strong __typeof(weakSelf)strongSelf = weakSelf; - if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) { - if (failure) { - failure(request, response, error); - } - [strongSelf clearActiveDownloadInformation]; - } - }]; - - self.af_activeImageDownloadReceipt = receipt; - } -} - -- (void)cancelImageDownloadTask { - if (self.af_activeImageDownloadReceipt != nil) { - [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:self.af_activeImageDownloadReceipt]; - [self clearActiveDownloadInformation]; - } -} - -- (void)clearActiveDownloadInformation { - self.af_activeImageDownloadReceipt = nil; -} - -- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest { - return [self.af_activeImageDownloadReceipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString]; -} - -@end - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h deleted file mode 100644 index febacfc..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h +++ /dev/null @@ -1,42 +0,0 @@ -// UIKit+AFNetworking.h -// -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#if TARGET_OS_IOS || TARGET_OS_TV -#import - -#ifndef _UIKIT_AFNETWORKING_ - #define _UIKIT_AFNETWORKING_ - -#if TARGET_OS_IOS - #import "AFAutoPurgingImageCache.h" - #import "AFImageDownloader.h" - #import "AFNetworkActivityIndicatorManager.h" - #import "UIRefreshControl+AFNetworking.h" - #import "UIWebView+AFNetworking.h" -#endif - - #import "UIActivityIndicatorView+AFNetworking.h" - #import "UIButton+AFNetworking.h" - #import "UIImageView+AFNetworking.h" - #import "UIProgressView+AFNetworking.h" -#endif /* _UIKIT_AFNETWORKING_ */ -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h deleted file mode 100644 index 8ea0a73..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h +++ /dev/null @@ -1,64 +0,0 @@ -// UIProgressView+AFNetworking.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import - -NS_ASSUME_NONNULL_BEGIN - - -/** - This category adds methods to the UIKit framework's `UIProgressView` class. The methods in this category provide support for binding the progress to the upload and download progress of a session task. - */ -@interface UIProgressView (AFNetworking) - -///------------------------------------ -/// @name Setting Session Task Progress -///------------------------------------ - -/** - Binds the progress to the upload progress of the specified session task. - - @param task The session task. - @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. - */ -- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task - animated:(BOOL)animated; - -/** - Binds the progress to the download progress of the specified session task. - - @param task The session task. - @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. - */ -- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task - animated:(BOOL)animated; - -@end - -NS_ASSUME_NONNULL_END - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m deleted file mode 100644 index 058755e..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m +++ /dev/null @@ -1,118 +0,0 @@ -// UIProgressView+AFNetworking.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "UIProgressView+AFNetworking.h" - -#import - -#if TARGET_OS_IOS || TARGET_OS_TV - -#import "AFURLSessionManager.h" - -static void * AFTaskCountOfBytesSentContext = &AFTaskCountOfBytesSentContext; -static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedContext; - -#pragma mark - - -@implementation UIProgressView (AFNetworking) - -- (BOOL)af_uploadProgressAnimated { - return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_uploadProgressAnimated)) boolValue]; -} - -- (void)af_setUploadProgressAnimated:(BOOL)animated { - objc_setAssociatedObject(self, @selector(af_uploadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (BOOL)af_downloadProgressAnimated { - return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_downloadProgressAnimated)) boolValue]; -} - -- (void)af_setDownloadProgressAnimated:(BOOL)animated { - objc_setAssociatedObject(self, @selector(af_downloadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -#pragma mark - - -- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task - animated:(BOOL)animated -{ - [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext]; - [task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext]; - - [self af_setUploadProgressAnimated:animated]; -} - -- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task - animated:(BOOL)animated -{ - [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext]; - [task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext]; - - [self af_setDownloadProgressAnimated:animated]; -} - -#pragma mark - NSKeyValueObserving - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(__unused NSDictionary *)change - context:(void *)context -{ - if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) { - if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) { - if ([object countOfBytesExpectedToSend] > 0) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated]; - }); - } - } - - if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) { - if ([object countOfBytesExpectedToReceive] > 0) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated]; - }); - } - } - - if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) { - if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) { - @try { - [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))]; - - if (context == AFTaskCountOfBytesSentContext) { - [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))]; - } - - if (context == AFTaskCountOfBytesReceivedContext) { - [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))]; - } - } - @catch (NSException * __unused exception) {} - } - } - } -} - -@end - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h deleted file mode 100644 index 215eafc..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h +++ /dev/null @@ -1,53 +0,0 @@ -// UIRefreshControl+AFNetworking.m -// -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#import - -#if TARGET_OS_IOS - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - This category adds methods to the UIKit framework's `UIRefreshControl` class. The methods in this category provide support for automatically beginning and ending refreshing depending on the loading state of a session task. - */ -@interface UIRefreshControl (AFNetworking) - -///----------------------------------- -/// @name Refreshing for Session Tasks -///----------------------------------- - -/** - Binds the refreshing state to the state of the specified task. - - @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled. - */ -- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; - -@end - -NS_ASSUME_NONNULL_END - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m deleted file mode 100644 index aba6d61..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m +++ /dev/null @@ -1,122 +0,0 @@ -// UIRefreshControl+AFNetworking.m -// -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "UIRefreshControl+AFNetworking.h" -#import - -#if TARGET_OS_IOS - -#import "AFURLSessionManager.h" - -@interface AFRefreshControlNotificationObserver : NSObject -@property (readonly, nonatomic, weak) UIRefreshControl *refreshControl; -- (instancetype)initWithActivityRefreshControl:(UIRefreshControl *)refreshControl; - -- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; - -@end - -@implementation UIRefreshControl (AFNetworking) - -- (AFRefreshControlNotificationObserver *)af_notificationObserver { - AFRefreshControlNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver)); - if (notificationObserver == nil) { - notificationObserver = [[AFRefreshControlNotificationObserver alloc] initWithActivityRefreshControl:self]; - objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - return notificationObserver; -} - -- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task { - [[self af_notificationObserver] setRefreshingWithStateOfTask:task]; -} - -@end - -@implementation AFRefreshControlNotificationObserver - -- (instancetype)initWithActivityRefreshControl:(UIRefreshControl *)refreshControl -{ - self = [super init]; - if (self) { - _refreshControl = refreshControl; - } - return self; -} - -- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task { - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - - [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; - [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; - [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; - - if (task) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wreceiver-is-weak" -#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak" - if (task.state == NSURLSessionTaskStateRunning) { - [self.refreshControl beginRefreshing]; - - [notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingTaskDidResumeNotification object:task]; - [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidCompleteNotification object:task]; - [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidSuspendNotification object:task]; - } else { - [self.refreshControl endRefreshing]; - } -#pragma clang diagnostic pop - } -} - -#pragma mark - - -- (void)af_beginRefreshing { - dispatch_async(dispatch_get_main_queue(), ^{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wreceiver-is-weak" - [self.refreshControl beginRefreshing]; -#pragma clang diagnostic pop - }); -} - -- (void)af_endRefreshing { - dispatch_async(dispatch_get_main_queue(), ^{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wreceiver-is-weak" - [self.refreshControl endRefreshing]; -#pragma clang diagnostic pop - }); -} - -#pragma mark - - -- (void)dealloc { - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - - [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; - [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; - [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; -} - -@end - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h deleted file mode 100644 index b9a56af..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h +++ /dev/null @@ -1,80 +0,0 @@ -// UIWebView+AFNetworking.h -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import - -#import - -#if TARGET_OS_IOS - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class AFHTTPSessionManager; - -/** - This category adds methods to the UIKit framework's `UIWebView` class. The methods in this category provide increased control over the request cycle, including progress monitoring and success / failure handling. - - @discussion When using these category methods, make sure to assign `delegate` for the web view, which implements `–webView:shouldStartLoadWithRequest:navigationType:` appropriately. This allows for tapped links to be loaded through AFNetworking, and can ensure that `canGoBack` & `canGoForward` update their values correctly. - */ -@interface UIWebView (AFNetworking) - -/** - The session manager used to download all requests. - */ -@property (nonatomic, strong) AFHTTPSessionManager *sessionManager; - -/** - Asynchronously loads the specified request. - - @param request A URL request identifying the location of the content to load. This must not be `nil`. - @param progress A progress object monitoring the current download progress. - @param success A block object to be executed when the request finishes loading successfully. This block returns the HTML string to be loaded by the web view, and takes two arguments: the response, and the response string. - @param failure A block object to be executed when the data task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred. - */ -- (void)loadRequest:(NSURLRequest *)request - progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress - success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success - failure:(nullable void (^)(NSError *error))failure; - -/** - Asynchronously loads the data associated with a particular request with a specified MIME type and text encoding. - - @param request A URL request identifying the location of the content to load. This must not be `nil`. - @param MIMEType The MIME type of the content. Defaults to the content type of the response if not specified. - @param textEncodingName The IANA encoding name, as in `utf-8` or `utf-16`. Defaults to the response text encoding if not specified. -@param progress A progress object monitoring the current download progress. - @param success A block object to be executed when the request finishes loading successfully. This block returns the data to be loaded by the web view and takes two arguments: the response, and the downloaded data. - @param failure A block object to be executed when the data task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred. - */ -- (void)loadRequest:(NSURLRequest *)request - MIMEType:(nullable NSString *)MIMEType - textEncodingName:(nullable NSString *)textEncodingName - progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress - success:(nullable NSData * (^)(NSHTTPURLResponse *response, NSData *data))success - failure:(nullable void (^)(NSError *error))failure; - -@end - -NS_ASSUME_NONNULL_END - -#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m deleted file mode 100644 index 751c499..0000000 --- a/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m +++ /dev/null @@ -1,162 +0,0 @@ -// UIWebView+AFNetworking.m -// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "UIWebView+AFNetworking.h" - -#import - -#if TARGET_OS_IOS - -#import "AFHTTPSessionManager.h" -#import "AFURLResponseSerialization.h" -#import "AFURLRequestSerialization.h" - -@interface UIWebView (_AFNetworking) -@property (readwrite, nonatomic, strong, setter = af_setURLSessionTask:) NSURLSessionDataTask *af_URLSessionTask; -@end - -@implementation UIWebView (_AFNetworking) - -- (NSURLSessionDataTask *)af_URLSessionTask { - return (NSURLSessionDataTask *)objc_getAssociatedObject(self, @selector(af_URLSessionTask)); -} - -- (void)af_setURLSessionTask:(NSURLSessionDataTask *)af_URLSessionTask { - objc_setAssociatedObject(self, @selector(af_URLSessionTask), af_URLSessionTask, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -@end - -#pragma mark - - -@implementation UIWebView (AFNetworking) - -- (AFHTTPSessionManager *)sessionManager { - static AFHTTPSessionManager *_af_defaultHTTPSessionManager = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _af_defaultHTTPSessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; - _af_defaultHTTPSessionManager.requestSerializer = [AFHTTPRequestSerializer serializer]; - _af_defaultHTTPSessionManager.responseSerializer = [AFHTTPResponseSerializer serializer]; - }); - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" - return objc_getAssociatedObject(self, @selector(sessionManager)) ?: _af_defaultHTTPSessionManager; -#pragma clang diagnostic pop -} - -- (void)setSessionManager:(AFHTTPSessionManager *)sessionManager { - objc_setAssociatedObject(self, @selector(sessionManager), sessionManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (AFHTTPResponseSerializer *)responseSerializer { - static AFHTTPResponseSerializer *_af_defaultResponseSerializer = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _af_defaultResponseSerializer = [AFHTTPResponseSerializer serializer]; - }); - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu" - return objc_getAssociatedObject(self, @selector(responseSerializer)) ?: _af_defaultResponseSerializer; -#pragma clang diagnostic pop -} - -- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { - objc_setAssociatedObject(self, @selector(responseSerializer), responseSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -#pragma mark - - -- (void)loadRequest:(NSURLRequest *)request - progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress - success:(NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success - failure:(void (^)(NSError *error))failure -{ - [self loadRequest:request MIMEType:nil textEncodingName:nil progress:progress success:^NSData *(NSHTTPURLResponse *response, NSData *data) { - NSStringEncoding stringEncoding = NSUTF8StringEncoding; - if (response.textEncodingName) { - CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); - if (encoding != kCFStringEncodingInvalidId) { - stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding); - } - } - - NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding]; - if (success) { - string = success(response, string); - } - - return [string dataUsingEncoding:stringEncoding]; - } failure:failure]; -} - -- (void)loadRequest:(NSURLRequest *)request - MIMEType:(NSString *)MIMEType - textEncodingName:(NSString *)textEncodingName - progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress - success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success - failure:(void (^)(NSError *error))failure -{ - NSParameterAssert(request); - - if (self.af_URLSessionTask.state == NSURLSessionTaskStateRunning || self.af_URLSessionTask.state == NSURLSessionTaskStateSuspended) { - [self.af_URLSessionTask cancel]; - } - self.af_URLSessionTask = nil; - - __weak __typeof(self)weakSelf = self; - NSURLSessionDataTask *dataTask; - dataTask = [self.sessionManager - GET:request.URL.absoluteString - parameters:nil - progress:nil - success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) { - __strong __typeof(weakSelf) strongSelf = weakSelf; - if (success) { - success((NSHTTPURLResponse *)task.response, responseObject); - } - [strongSelf loadData:responseObject MIMEType:MIMEType textEncodingName:textEncodingName baseURL:[task.currentRequest URL]]; - - if ([strongSelf.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) { - [strongSelf.delegate webViewDidFinishLoad:strongSelf]; - } - } - failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) { - if (failure) { - failure(error); - } - }]; - self.af_URLSessionTask = dataTask; - if (progress != nil) { - *progress = [self.sessionManager downloadProgressForTask:dataTask]; - } - [self.af_URLSessionTask resume]; - - if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) { - [self.delegate webViewDidStartLoad:self]; - } -} - -@end - -#endif \ No newline at end of file diff --git a/Pods/DACircularProgress/DACircularProgress/DACircularProgressView.h b/Pods/DACircularProgress/DACircularProgress/DACircularProgressView.h deleted file mode 100644 index 391b449..0000000 --- a/Pods/DACircularProgress/DACircularProgress/DACircularProgressView.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// DACircularProgressView.h -// DACircularProgress -// -// Created by Daniel Amitay on 2/6/12. -// Copyright (c) 2012 Daniel Amitay. All rights reserved. -// - -#import - -@interface DACircularProgressView : UIView - -@property(nonatomic, strong) UIColor *trackTintColor UI_APPEARANCE_SELECTOR; -@property(nonatomic, strong) UIColor *progressTintColor UI_APPEARANCE_SELECTOR; -@property(nonatomic, strong) UIColor *innerTintColor UI_APPEARANCE_SELECTOR; -@property(nonatomic) NSInteger roundedCorners UI_APPEARANCE_SELECTOR; // Can not use BOOL with UI_APPEARANCE_SELECTOR :-( -@property(nonatomic) CGFloat thicknessRatio UI_APPEARANCE_SELECTOR; -@property(nonatomic) NSInteger clockwiseProgress UI_APPEARANCE_SELECTOR; // Can not use BOOL with UI_APPEARANCE_SELECTOR :-( -@property(nonatomic) CGFloat progress; - -@property(nonatomic) CGFloat indeterminateDuration UI_APPEARANCE_SELECTOR; -@property(nonatomic) NSInteger indeterminate UI_APPEARANCE_SELECTOR; // Can not use BOOL with UI_APPEARANCE_SELECTOR :-( - -- (void)setProgress:(CGFloat)progress animated:(BOOL)animated; -- (void)setProgress:(CGFloat)progress animated:(BOOL)animated initialDelay:(CFTimeInterval)initialDelay; -- (void)setProgress:(CGFloat)progress animated:(BOOL)animated initialDelay:(CFTimeInterval)initialDelay withDuration:(CFTimeInterval)duration; - -@end diff --git a/Pods/DACircularProgress/DACircularProgress/DACircularProgressView.m b/Pods/DACircularProgress/DACircularProgress/DACircularProgressView.m deleted file mode 100644 index ea39dcd..0000000 --- a/Pods/DACircularProgress/DACircularProgress/DACircularProgressView.m +++ /dev/null @@ -1,321 +0,0 @@ -// -// DACircularProgressView.m -// DACircularProgress -// -// Created by Daniel Amitay on 2/6/12. -// Copyright (c) 2012 Daniel Amitay. All rights reserved. -// - -#import "DACircularProgressView.h" - -#import - -@interface DACircularProgressLayer : CALayer - -@property(nonatomic, strong) UIColor *trackTintColor; -@property(nonatomic, strong) UIColor *progressTintColor; -@property(nonatomic, strong) UIColor *innerTintColor; -@property(nonatomic) NSInteger roundedCorners; -@property(nonatomic) CGFloat thicknessRatio; -@property(nonatomic) CGFloat progress; -@property(nonatomic) NSInteger clockwiseProgress; - -@end - -@implementation DACircularProgressLayer - -@dynamic trackTintColor; -@dynamic progressTintColor; -@dynamic innerTintColor; -@dynamic roundedCorners; -@dynamic thicknessRatio; -@dynamic progress; -@dynamic clockwiseProgress; - -+ (BOOL)needsDisplayForKey:(NSString *)key -{ - if ([key isEqualToString:@"progress"]) { - return YES; - } else { - return [super needsDisplayForKey:key]; - } -} - -- (void)drawInContext:(CGContextRef)context -{ - CGRect rect = self.bounds; - CGPoint centerPoint = CGPointMake(rect.size.width / 2.0f, rect.size.height / 2.0f); - CGFloat radius = MIN(rect.size.height, rect.size.width) / 2.0f; - - BOOL clockwise = (self.clockwiseProgress != 0); - - CGFloat progress = MIN(self.progress, 1.0f - FLT_EPSILON); - CGFloat radians = 0; - if (clockwise) { - radians = (float)((progress * 2.0f * M_PI) - M_PI_2); - } else { - radians = (float)(3 * M_PI_2 - (progress * 2.0f * M_PI)); - } - - CGContextSetFillColorWithColor(context, self.trackTintColor.CGColor); - CGMutablePathRef trackPath = CGPathCreateMutable(); - CGPathMoveToPoint(trackPath, NULL, centerPoint.x, centerPoint.y); - CGPathAddArc(trackPath, NULL, centerPoint.x, centerPoint.y, radius, (float)(2.0f * M_PI), 0.0f, TRUE); - CGPathCloseSubpath(trackPath); - CGContextAddPath(context, trackPath); - CGContextFillPath(context); - CGPathRelease(trackPath); - - if (progress > 0.0f) { - CGContextSetFillColorWithColor(context, self.progressTintColor.CGColor); - CGMutablePathRef progressPath = CGPathCreateMutable(); - CGPathMoveToPoint(progressPath, NULL, centerPoint.x, centerPoint.y); - CGPathAddArc(progressPath, NULL, centerPoint.x, centerPoint.y, radius, (float)(3.0f * M_PI_2), radians, !clockwise); - CGPathCloseSubpath(progressPath); - CGContextAddPath(context, progressPath); - CGContextFillPath(context); - CGPathRelease(progressPath); - } - - if (progress > 0.0f && self.roundedCorners) { - CGFloat pathWidth = radius * self.thicknessRatio; - CGFloat xOffset = radius * (1.0f + ((1.0f - (self.thicknessRatio / 2.0f)) * cosf(radians))); - CGFloat yOffset = radius * (1.0f + ((1.0f - (self.thicknessRatio / 2.0f)) * sinf(radians))); - CGPoint endPoint = CGPointMake(xOffset, yOffset); - - CGRect startEllipseRect = (CGRect) { - .origin.x = centerPoint.x - pathWidth / 2.0f, - .origin.y = 0.0f, - .size.width = pathWidth, - .size.height = pathWidth - }; - CGContextAddEllipseInRect(context, startEllipseRect); - CGContextFillPath(context); - - CGRect endEllipseRect = (CGRect) { - .origin.x = endPoint.x - pathWidth / 2.0f, - .origin.y = endPoint.y - pathWidth / 2.0f, - .size.width = pathWidth, - .size.height = pathWidth - }; - CGContextAddEllipseInRect(context, endEllipseRect); - CGContextFillPath(context); - } - - CGContextSetBlendMode(context, kCGBlendModeClear); - CGFloat innerRadius = radius * (1.0f - self.thicknessRatio); - CGRect clearRect = (CGRect) { - .origin.x = centerPoint.x - innerRadius, - .origin.y = centerPoint.y - innerRadius, - .size.width = innerRadius * 2.0f, - .size.height = innerRadius * 2.0f - }; - CGContextAddEllipseInRect(context, clearRect); - CGContextFillPath(context); - - if (self.innerTintColor) { - CGContextSetBlendMode(context, kCGBlendModeNormal); - CGContextSetFillColorWithColor(context, [self.innerTintColor CGColor]); - CGContextAddEllipseInRect(context, clearRect); - CGContextFillPath(context); - } -} - -@end - -@interface DACircularProgressView () - -@end - -@implementation DACircularProgressView - -+ (void) initialize -{ - if (self == [DACircularProgressView class]) { - DACircularProgressView *circularProgressViewAppearance = [DACircularProgressView appearance]; - [circularProgressViewAppearance setTrackTintColor:[[UIColor whiteColor] colorWithAlphaComponent:0.3f]]; - [circularProgressViewAppearance setProgressTintColor:[UIColor whiteColor]]; - [circularProgressViewAppearance setInnerTintColor:nil]; - [circularProgressViewAppearance setBackgroundColor:[UIColor clearColor]]; - [circularProgressViewAppearance setThicknessRatio:0.3f]; - [circularProgressViewAppearance setRoundedCorners:NO]; - [circularProgressViewAppearance setClockwiseProgress:YES]; - - [circularProgressViewAppearance setIndeterminateDuration:2.0f]; - [circularProgressViewAppearance setIndeterminate:NO]; - } -} - -+ (Class)layerClass -{ - return [DACircularProgressLayer class]; -} - -- (DACircularProgressLayer *)circularProgressLayer -{ - return (DACircularProgressLayer *)self.layer; -} - -- (id)init -{ - return [super initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 40.0f)]; -} - -- (void)didMoveToWindow -{ - CGFloat windowContentsScale = self.window.screen.scale; - self.circularProgressLayer.contentsScale = windowContentsScale; - [self.circularProgressLayer setNeedsDisplay]; -} - - -#pragma mark - Progress - -- (CGFloat)progress -{ - return self.circularProgressLayer.progress; -} - -- (void)setProgress:(CGFloat)progress -{ - [self setProgress:progress animated:NO]; -} - -- (void)setProgress:(CGFloat)progress animated:(BOOL)animated -{ - [self setProgress:progress animated:animated initialDelay:0.0]; -} - -- (void)setProgress:(CGFloat)progress - animated:(BOOL)animated - initialDelay:(CFTimeInterval)initialDelay -{ - CGFloat pinnedProgress = MIN(MAX(progress, 0.0f), 1.0f); - [self setProgress:progress - animated:animated - initialDelay:initialDelay - withDuration:fabs(self.progress - pinnedProgress)]; -} - -- (void)setProgress:(CGFloat)progress - animated:(BOOL)animated - initialDelay:(CFTimeInterval)initialDelay - withDuration:(CFTimeInterval)duration -{ - [self.layer removeAnimationForKey:@"indeterminateAnimation"]; - [self.circularProgressLayer removeAnimationForKey:@"progress"]; - - CGFloat pinnedProgress = MIN(MAX(progress, 0.0f), 1.0f); - if (animated) { - CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"progress"]; - animation.duration = duration; - animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; - animation.fillMode = kCAFillModeForwards; - animation.fromValue = [NSNumber numberWithFloat:self.progress]; - animation.toValue = [NSNumber numberWithFloat:pinnedProgress]; - animation.beginTime = CACurrentMediaTime() + initialDelay; - animation.delegate = self; - [self.circularProgressLayer addAnimation:animation forKey:@"progress"]; - } else { - [self.circularProgressLayer setNeedsDisplay]; - self.circularProgressLayer.progress = pinnedProgress; - } -} - -- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag -{ - NSNumber *pinnedProgressNumber = [animation valueForKey:@"toValue"]; - self.circularProgressLayer.progress = [pinnedProgressNumber floatValue]; -} - - -#pragma mark - UIAppearance methods - -- (UIColor *)trackTintColor -{ - return self.circularProgressLayer.trackTintColor; -} - -- (void)setTrackTintColor:(UIColor *)trackTintColor -{ - self.circularProgressLayer.trackTintColor = trackTintColor; - [self.circularProgressLayer setNeedsDisplay]; -} - -- (UIColor *)progressTintColor -{ - return self.circularProgressLayer.progressTintColor; -} - -- (void)setProgressTintColor:(UIColor *)progressTintColor -{ - self.circularProgressLayer.progressTintColor = progressTintColor; - [self.circularProgressLayer setNeedsDisplay]; -} - -- (UIColor *)innerTintColor -{ - return self.circularProgressLayer.innerTintColor; -} - -- (void)setInnerTintColor:(UIColor *)innerTintColor -{ - self.circularProgressLayer.innerTintColor = innerTintColor; - [self.circularProgressLayer setNeedsDisplay]; -} - -- (NSInteger)roundedCorners -{ - return self.roundedCorners; -} - -- (void)setRoundedCorners:(NSInteger)roundedCorners -{ - self.circularProgressLayer.roundedCorners = roundedCorners; - [self.circularProgressLayer setNeedsDisplay]; -} - -- (CGFloat)thicknessRatio -{ - return self.circularProgressLayer.thicknessRatio; -} - -- (void)setThicknessRatio:(CGFloat)thicknessRatio -{ - self.circularProgressLayer.thicknessRatio = MIN(MAX(thicknessRatio, 0.f), 1.f); - [self.circularProgressLayer setNeedsDisplay]; -} - -- (NSInteger)indeterminate -{ - CAAnimation *spinAnimation = [self.layer animationForKey:@"indeterminateAnimation"]; - return (spinAnimation == nil ? 0 : 1); -} - -- (void)setIndeterminate:(NSInteger)indeterminate -{ - if (indeterminate) { - if (!self.indeterminate) { - CABasicAnimation *spinAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; - spinAnimation.byValue = [NSNumber numberWithDouble:indeterminate > 0 ? 2.0f*M_PI : -2.0f*M_PI]; - spinAnimation.duration = self.indeterminateDuration; - spinAnimation.repeatCount = HUGE_VALF; - [self.layer addAnimation:spinAnimation forKey:@"indeterminateAnimation"]; - } - } else { - [self.layer removeAnimationForKey:@"indeterminateAnimation"]; - } -} - -- (NSInteger)clockwiseProgress -{ - return self.circularProgressLayer.clockwiseProgress; -} - -- (void)setClockwiseProgress:(NSInteger)clockwiseProgres -{ - self.circularProgressLayer.clockwiseProgress = clockwiseProgres; - [self.circularProgressLayer setNeedsDisplay]; -} - -@end diff --git a/Pods/DACircularProgress/DACircularProgress/DALabeledCircularProgressView.h b/Pods/DACircularProgress/DACircularProgress/DALabeledCircularProgressView.h deleted file mode 100644 index 1d4e56c..0000000 --- a/Pods/DACircularProgress/DACircularProgress/DALabeledCircularProgressView.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// DALabeledCircularProgressView.h -// DACircularProgressExample -// -// Created by Josh Sklar on 4/8/14. -// Copyright (c) 2014 Shout Messenger. All rights reserved. -// - -#import "DACircularProgressView.h" - -/** - @class DALabeledCircularProgressView - - @brief Subclass of DACircularProgressView that adds a UILabel. - */ -@interface DALabeledCircularProgressView : DACircularProgressView - -/** - UILabel placed right on the DACircularProgressView. - */ -@property (strong, nonatomic) UILabel *progressLabel; - -@end diff --git a/Pods/DACircularProgress/DACircularProgress/DALabeledCircularProgressView.m b/Pods/DACircularProgress/DACircularProgress/DALabeledCircularProgressView.m deleted file mode 100644 index c0e2bda..0000000 --- a/Pods/DACircularProgress/DACircularProgress/DALabeledCircularProgressView.m +++ /dev/null @@ -1,47 +0,0 @@ -// -// DALabeledCircularProgressView.m -// DACircularProgressExample -// -// Created by Josh Sklar on 4/8/14. -// Copyright (c) 2014 Shout Messenger. All rights reserved. -// - -#import "DALabeledCircularProgressView.h" - -@implementation DALabeledCircularProgressView - -- (id)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - if (self) { - [self initializeLabel]; - } - return self; -} - -- (id)initWithCoder:(NSCoder *)aDecoder -{ - self = [super initWithCoder:aDecoder]; - if (self) { - [self initializeLabel]; - } - return self; -} - - -#pragma mark - Internal methods - -/** - Creates and initializes - -[DALabeledCircularProgressView progressLabel]. - */ -- (void)initializeLabel -{ - self.progressLabel = [[UILabel alloc] initWithFrame:self.bounds]; - self.progressLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - self.progressLabel.textAlignment = NSTextAlignmentCenter; - self.progressLabel.backgroundColor = [UIColor clearColor]; - [self addSubview:self.progressLabel]; -} - -@end diff --git a/Pods/DACircularProgress/LICENSE.md b/Pods/DACircularProgress/LICENSE.md deleted file mode 100755 index 94d0610..0000000 --- a/Pods/DACircularProgress/LICENSE.md +++ /dev/null @@ -1,23 +0,0 @@ -# License - -## MIT License - -Copyright (c) 2013 Daniel Amitay (http://danielamitay.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/Pods/DACircularProgress/README.md b/Pods/DACircularProgress/README.md deleted file mode 100755 index 3c40031..0000000 --- a/Pods/DACircularProgress/README.md +++ /dev/null @@ -1,77 +0,0 @@ -## DACircularProgress - -`DACircularProgress` is a `UIView` subclass with circular `UIProgressView` properties. - -It was originally built to be an imitation of Facebook's photo progress indicator. - -View the included example project for a demonstration. - -![Screenshot](https://github.com/danielamitay/DACircularProgress/raw/master/screenshot.png) - -## Installation - -To use `DACircularProgress`: - -- Copy over the `DACircularProgress` folder to your project folder. -- Make sure that your project includes ``. -- `#import "DACircularProgressView.h"` - -### Example Code - -```objective-c - -self.progressView = [[DACircularProgressView alloc] initWithFrame:CGRectMake(140.0f, 30.0f, 40.0f, 40.0f)]; -self.progressView.roundedCorners = YES; -self.progressView.trackTintColor = [UIColor clearColor]; -[self.view addSubview:self.progressView]; -``` - -- You can also use Interface Builder by adding a `UIView` element and setting its class to `DACircularProgress` - -## Notes - -### Compatibility - -iOS5.0+ - -### Automatic Reference Counting (ARC) support - -`DACircularProgress` was made with ARC enabled by default. - -## Contact - -- [Personal website](http://danielamitay.com) -- [GitHub](http://github.com/danielamitay) -- [Twitter](http://twitter.com/danielamitay) -- [LinkedIn](http://www.linkedin.com/in/danielamitay) -- [Email](hello@danielamitay.com) - -If you use/enjoy `DACircularProgress`, let me know! - -## Credits - -`DACircularProgress` is brought to you by [Daniel Amitay](http://www.amitay.us) and [contributors to the project](https://github.com/danielamitay/DACircularProgress/contributors). A special thanks to [Cédric Luthi](https://github.com/0xced) for a significant amount of changes. If you have feature suggestions or bug reports, feel free to help out by sending pull requests or by [creating new issues](https://github.com/danielamitay/DACircularProgress/issues/new). - -## License - -### MIT License - -Copyright (c) 2013 Daniel Amitay (http://danielamitay.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/Pods/DKNightVersion/DKNightVersion/ColorTable/DKColorTable.h b/Pods/DKNightVersion/DKNightVersion/ColorTable/DKColorTable.h deleted file mode 100644 index adea6de..0000000 --- a/Pods/DKNightVersion/DKNightVersion/ColorTable/DKColorTable.h +++ /dev/null @@ -1,87 +0,0 @@ -// -// DKColorTable.h -// DKNightVersion -// -// Created by Draveness on 15/12/11. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import -#import "DKNightVersionManager.h" - -/** - * A convinient macro to create DKColorPicker block. - * - * @param key Key for corresponding entry in table - * - * @return DKColorPicker - */ -#define DKColorPickerWithKey(key) [[DKColorTable sharedColorTable] pickerWithKey:@#key] - -/** - * DKColorTable is a new feature in 2.x, which providing you a very convinient and - * delightful approach to manage all your color in an iOS project. Besides that, we - * support multiple themes with DKColorTable, change your `DKColorTable.txt` file - * like this: - * - * Ex: - * - * NORMAL NIGHT RED - * #ffffff #343434 #ff0000 BG - * #aaaaaa #313131 #ff0000 SEP - * - * And you can directly change `[DKNightVersionManager sharedManager].themeVersion` to - * what you want, like: `RED` `NORMAL` and `NIGHT`. And trigger to post notification - * and update corresponding color. - */ -@interface DKColorTable : NSObject - -/** - * Call `- reloadColorTable` will trigger `DKColorTable` to load this file, - * default is `DKColorTable.txt`. Don't need to call `- reloadColorTable` after - * setting this property, cuz we have already do it for you. - */ -@property (nonatomic, strong) NSString *file; - -/** - * An array of DKThemeVersion, order is exactly the same in `file`. - */ -@property (nonatomic, strong, readonly) NSArray *themes; - -/** - * Return color table instance, you MUST use this method instead of `- init`, - * `- init` method may have negative impact on your performance. - * - * @return An instance of DKColorTable - */ -+ (instancetype)sharedColorTable; - -/** - * Reload `file` into memory, and reconstrcut the whole color table. This method - * will clear color table and use current `file` to load color table again. - */ -- (void)reloadColorTable; - -/** - * Return a `DKColorPicker` with `key`, but I suggest you use marcho `DKColorPickerWithKey(key)` - * instead of calling this method. - * - * Ex: - * - * NORMAL NIGHT - * #ffffff #343434 BG - * #aaaaaa #313131 SEP - * - * self.view.dk_backgroundColorPicker = DKColorPickerWithKey(BG); - * - * If current themeVersion is NORMAL, view's background color will be set to #ffffff. When theme - * changes, it will automatically reload color from global color table and update current color - * again. - * - * @param key Which indicates the entry you refer to - * - * @return An DKColorPicker block - */ -- (DKColorPicker)pickerWithKey:(NSString *)key; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/ColorTable/DKColorTable.m b/Pods/DKNightVersion/DKNightVersion/ColorTable/DKColorTable.m deleted file mode 100755 index bb366b2..0000000 --- a/Pods/DKNightVersion/DKNightVersion/ColorTable/DKColorTable.m +++ /dev/null @@ -1,160 +0,0 @@ -// -// DKColorTable.m -// DKNightVersion -// -// Created by Draveness on 15/12/11. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import "DKColorTable.h" - -@interface DKColorTable () - -@property (nonatomic, strong) NSMutableDictionary *> *table; -@property (nonatomic, strong, readwrite) NSArray *themes; - -@end - -@implementation DKColorTable - -UIColor *DKColorFromRGB(NSUInteger hex) { - return [UIColor colorWithRed:((CGFloat)((hex >> 16) & 0xFF)/255.0) green:((CGFloat)((hex >> 8) & 0xFF)/255.0) blue:((CGFloat)(hex & 0xFF)/255.0) alpha:1.0]; -} - -UIColor *DKColorFromRGBA(NSUInteger hex) { - return [UIColor colorWithRed:((CGFloat)((hex >> 24) & 0xFF)/255.0) green:((CGFloat)((hex >> 16) & 0xFF)/255.0) blue:((CGFloat)((hex >> 8) & 0xFF)/255.0) alpha:((CGFloat)(hex & 0xFF)/255.0)]; -} - -+ (instancetype)sharedColorTable { - static DKColorTable *sharedInstance = nil; - static dispatch_once_t oncePredicate; - dispatch_once(&oncePredicate, ^{ - sharedInstance = [[DKColorTable alloc] init]; - sharedInstance.file = @"DKColorTable.txt"; - }); - return sharedInstance; -} - -- (void)reloadColorTable { - // Clear previos color table - self.table = nil; - self.themes = nil; - - // Load color table file - NSString *filepath = [[NSBundle mainBundle] pathForResource:self.file.stringByDeletingPathExtension ofType:self.file.pathExtension]; - NSError *error; - NSString *fileContents = [NSString stringWithContentsOfFile:filepath - encoding:NSUTF8StringEncoding - error:&error]; - - if (error) - NSLog(@"Error reading file: %@", error.localizedDescription); - -// NSLog(@"DKColorTable:\n%@", fileContents); - - - NSMutableArray *entries = [[fileContents componentsSeparatedByString:@"\n"] mutableCopy]; - [entries removeObjectAtIndex:0]; // Remove theme entry - - self.themes = [self themesFromContents:fileContents]; - - // Add entry to color table - for (NSString *entry in entries) { - NSArray *colors = [self colorsFromEntry:entry]; - NSString *key = [self keyFromEntry:entry]; - - [self addEntryWithKey:key colors:colors themes:self.themes]; - } -} - -- (NSArray *)themesFromContents:(NSString *)content { - NSString *rawThemes = [content componentsSeparatedByString:@"\n"].firstObject; - return [self separateString:rawThemes]; -} - -- (NSArray *)colorsFromEntry:(NSString *)entry { - NSMutableArray *colors = [[self separateString:entry] mutableCopy]; - [colors removeLastObject]; - NSMutableArray *result = [@[] mutableCopy]; - for (NSString *number in colors) { - [result addObject:[self colorFromString:number]]; - } - return result; -} - -- (NSString *)keyFromEntry:(NSString *)entry { - return [self separateString:entry].lastObject; -} - -- (void)addEntryWithKey:(NSString *)key colors:(NSArray *)colors themes:(NSArray *)themes { - NSParameterAssert(themes.count == colors.count); - - __block NSMutableDictionary *themeToColorDictionary = [@{} mutableCopy]; - - [themes enumerateObjectsUsingBlock:^(NSString * _Nonnull theme, NSUInteger idx, BOOL * _Nonnull stop) { - [themeToColorDictionary setValue:colors[idx] forKey:theme]; - }]; - - [self.table setValue:themeToColorDictionary forKey:key]; -} - -- (DKColorPicker)pickerWithKey:(NSString *)key { - NSParameterAssert(key); - - NSDictionary *themeToColorDictionary = [self.table valueForKey:key]; - DKColorPicker picker = ^(DKThemeVersion *themeVersion) { - return [themeToColorDictionary valueForKey:themeVersion]; - }; - return picker; - -} - -#pragma mark - Getter/Setter - -- (NSMutableDictionary *)table { - if (!_table) { - _table = [[NSMutableDictionary alloc] init]; - } - return _table; -} - -- (void)setFile:(NSString *)file { - _file = file; - [self reloadColorTable]; -} - -#pragma mark - Helper - -- (UIColor*)colorFromString:(NSString*)hexStr { - hexStr = [hexStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - if([hexStr hasPrefix:@"0x"]) { - hexStr = [hexStr substringFromIndex:2]; - } - if([hexStr hasPrefix:@"#"]) { - hexStr = [hexStr substringFromIndex:1]; - } - - NSUInteger hex = [self intFromHexString:hexStr]; - if(hexStr.length > 6) { - return DKColorFromRGBA(hex); - } - - return DKColorFromRGB(hex); -} - -- (NSUInteger)intFromHexString:(NSString *)hexStr { - unsigned int hexInt = 0; - - NSScanner *scanner = [NSScanner scannerWithString:hexStr]; - - [scanner scanHexInt:&hexInt]; - - return hexInt; -} - -- (NSArray *)separateString:(NSString *)string { - NSArray *array = [string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - return[array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != ''"]]; -} - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/ColorTable/DKColorTable.txt b/Pods/DKNightVersion/DKNightVersion/ColorTable/DKColorTable.txt deleted file mode 100644 index 7628bed..0000000 --- a/Pods/DKNightVersion/DKNightVersion/ColorTable/DKColorTable.txt +++ /dev/null @@ -1,6 +0,0 @@ -NORMAL NIGHT RED -#ffffff #343434 #fafafa BG -#aaaaaa #313131 #aaaaaa SEP -#0000ff #ffffff #fa0000 TINT -#000000 #ffffff #000000 TEXT -#ffffff #444444 #ffffff BAR \ No newline at end of file diff --git a/Pods/DKNightVersion/DKNightVersion/Core/DKColor.h b/Pods/DKNightVersion/DKNightVersion/Core/DKColor.h deleted file mode 100644 index 555b735..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Core/DKColor.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// DKColor.h -// DKNightVersion -// -// Created by Draveness on 15/12/9. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import - -typedef NSString DKThemeVersion; - -typedef UIColor *(^DKColorPicker)(DKThemeVersion *themeVersion); - -DKColorPicker DKColorPickerWithRGB(NSUInteger normal, ...); -DKColorPicker DKColorPickerWithColors(UIColor *normalColor, ...); - -@interface DKColor : NSObject - -+ (DKColorPicker)colorPickerWithUIColor:(UIColor *)color; - -+ (DKColorPicker)colorPickerWithWhite:(CGFloat)white alpha:(CGFloat)alpha; -+ (DKColorPicker)colorPickerWithHue:(CGFloat)hue saturation:(CGFloat)saturation brightness:(CGFloat)brightness alpha:(CGFloat)alpha; -+ (DKColorPicker)colorPickerWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; -+ (DKColorPicker)colorPickerWithCGColor:(CGColorRef)cgColor; -+ (DKColorPicker)colorPickerWithPatternImage:(UIImage *)image; -#if __has_include() -+ (DKColorPicker)colorPickerWithCIColor:(CIColor *)ciColor NS_AVAILABLE_IOS(5_0); -#endif - -+ (DKColorPicker)blackColor; -+ (DKColorPicker)darkGrayColor; -+ (DKColorPicker)lightGrayColor; -+ (DKColorPicker)whiteColor; -+ (DKColorPicker)grayColor; -+ (DKColorPicker)redColor; -+ (DKColorPicker)greenColor; -+ (DKColorPicker)blueColor; -+ (DKColorPicker)cyanColor; -+ (DKColorPicker)yellowColor; -+ (DKColorPicker)magentaColor; -+ (DKColorPicker)orangeColor; -+ (DKColorPicker)purpleColor; -+ (DKColorPicker)brownColor; -+ (DKColorPicker)clearColor; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Core/DKColor.m b/Pods/DKNightVersion/DKNightVersion/Core/DKColor.m deleted file mode 100644 index 9f4c34d..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Core/DKColor.m +++ /dev/null @@ -1,162 +0,0 @@ -// -// DKColor.m -// DKNightVersion -// -// Created by Draveness on 15/12/9. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import "DKColor.h" -#import "DKNightVersionManager.h" -#import "DKColorTable.h" - -@implementation DKColor - -DKColorPicker DKColorPickerWithRGB(NSUInteger normal, ...) { - UIColor *normalColor = [UIColor colorWithRed:((float)((normal & 0xFF0000) >> 16))/255.0 green:((float)((normal & 0xFF00) >> 8))/255.0 blue:((float)(normal & 0xFF))/255.0 alpha:1.0]; - - NSArray *themes = [DKColorTable sharedColorTable].themes; - NSMutableArray *colors = [[NSMutableArray alloc] initWithCapacity:themes.count]; - [colors addObject:normalColor]; - NSUInteger num_args = themes.count - 1; - va_list rgbs; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wvarargs" - va_start(rgbs, num_args); -#pragma clang diagnostic pop - for (NSUInteger i = 0; i < num_args; i++) { - NSUInteger rgb = va_arg(rgbs, NSUInteger); - UIColor *color = [UIColor colorWithRed:((float)((rgb & 0xFF0000) >> 16))/255.0 green:((float)((rgb & 0xFF00) >> 8))/255.0 blue:((float)(rgb & 0xFF))/255.0 alpha:1.0]; - [colors addObject:color]; - } - va_end(rgbs); - - return ^(DKThemeVersion *themeVersion) { - NSUInteger index = [themes indexOfObject:themeVersion]; - return colors[index]; - }; -} - -DKColorPicker DKColorPickerWithColors(UIColor *normalColor, ...) { - NSArray *themes = [DKColorTable sharedColorTable].themes; - NSMutableArray *colors = [[NSMutableArray alloc] initWithCapacity:themes.count]; - [colors addObject:normalColor]; - NSUInteger num_args = themes.count - 1; - va_list colors_list; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wvarargs" - va_start(colors_list, num_args); -#pragma clang diagnostic pop - - for (NSUInteger i = 0; i < num_args; i++) { - UIColor *color = va_arg(colors_list, UIColor *); - [colors addObject:color]; - } - va_end(colors_list); - - return ^(DKThemeVersion *themeVersion) { - NSUInteger index = [themes indexOfObject:themeVersion]; - return colors[index]; - }; -} - -+ (DKColorPicker)pickerWithNormalColor:(UIColor *)normalColor nightColor:(UIColor *)nightColor { - return ^(DKThemeVersion *themeVersion) { - return [themeVersion isEqualToString:DKThemeVersionNormal] ? normalColor : nightColor; - }; -} - -+ (DKColorPicker)colorPickerWithUIColor:(UIColor *)color { - return ^(DKThemeVersion *themeVersion) { - return color; - }; -} - -+ (DKColorPicker)blackColor { - return [self colorPickerWithUIColor:[UIColor blackColor]]; -} - -+ (DKColorPicker)darkGrayColor { - return [self colorPickerWithUIColor:[UIColor darkGrayColor]]; -} - -+ (DKColorPicker)lightGrayColor { - return [self colorPickerWithUIColor:[UIColor lightGrayColor]]; -} - -+ (DKColorPicker)whiteColor { - return [self colorPickerWithUIColor:[UIColor whiteColor]]; -} - -+ (DKColorPicker)grayColor { - return [self colorPickerWithUIColor:[UIColor grayColor]]; -} - -+ (DKColorPicker)redColor { - return [self colorPickerWithUIColor:[UIColor redColor]]; -} - -+ (DKColorPicker)greenColor { - return [self colorPickerWithUIColor:[UIColor greenColor]]; -} - -+ (DKColorPicker)blueColor { - return [self colorPickerWithUIColor:[UIColor blueColor]]; -} - -+ (DKColorPicker)cyanColor { - return [self colorPickerWithUIColor:[UIColor cyanColor]]; -} - -+ (DKColorPicker)yellowColor { - return [self colorPickerWithUIColor:[UIColor yellowColor]]; -} - -+ (DKColorPicker)magentaColor { - return [self colorPickerWithUIColor:[UIColor magentaColor]]; -} - -+ (DKColorPicker)orangeColor { - return [self colorPickerWithUIColor:[UIColor orangeColor]]; -} - -+ (DKColorPicker)purpleColor { - return [self colorPickerWithUIColor:[UIColor purpleColor]]; -} - -+ (DKColorPicker)brownColor { - return [self colorPickerWithUIColor:[UIColor brownColor]]; -} - -+ (DKColorPicker)clearColor { - return [self colorPickerWithUIColor:[UIColor clearColor]]; -} - -+ (DKColorPicker)colorPickerWithWhite:(CGFloat)white alpha:(CGFloat)alpha { - return [self colorPickerWithUIColor:[UIColor colorWithWhite:white alpha:alpha]]; -} - -+ (DKColorPicker)colorPickerWithHue:(CGFloat)hue saturation:(CGFloat)saturation brightness:(CGFloat)brightness alpha:(CGFloat)alpha { - return [self colorPickerWithUIColor:[UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:alpha]]; -} - -+ (DKColorPicker)colorPickerWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha { - return [self colorPickerWithUIColor:[UIColor colorWithRed:red green:green blue:blue alpha:alpha]]; -} - -+ (DKColorPicker)colorPickerWithCGColor:(CGColorRef)cgColor { - return [self colorPickerWithUIColor:[UIColor colorWithCGColor:cgColor]]; -} - -+ (DKColorPicker)colorPickerWithPatternImage:(UIImage *)image { - return [self colorPickerWithUIColor:[UIColor colorWithPatternImage:image]]; -} - -#if __has_include() -+ (DKColorPicker)colorPickerWithCIColor:(CIColor *)ciColor NS_AVAILABLE_IOS(5_0) { - return [self colorPickerWithUIColor:[UIColor colorWithCIColor:ciColor]]; -} -#endif - -@end - diff --git a/Pods/DKNightVersion/DKNightVersion/Core/DKImage.h b/Pods/DKNightVersion/DKNightVersion/Core/DKImage.h deleted file mode 100644 index 3216faa..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Core/DKImage.h +++ /dev/null @@ -1,84 +0,0 @@ -// -// DKImage.h -// DKNightVersion -// -// Created by Draveness on 15/12/10. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import - -typedef NSString DKThemeVersion; - -typedef UIImage *(^DKImagePicker)(DKThemeVersion *themeVersion); - -/** - * A C function takes an array of images return a image picker, the - * order of the images is just like the themes order in DKColorTable.txt - * file. - * - * @param normalImage Image when current themeVersion is DKThemeVersionNormal - * @param ... Other images, the order is the same as DKColorTable - * - * @return A DKImagePicker - */ -DKImagePicker DKImagePickerWithImages(UIImage *normalImage, ...); - -/** - * A C function takes an array of names return a image picker, the - * order of the images is just like the themes order in DKColorTable.txt - * file. - * - * @param normalName Names when current themeVersion is DKThemeVersionNormal - * @param ... Other names, the order is the same as DKColorTable - * - * @return A DKImagePicker - */ -DKImagePicker DKImagePickerWithNames(NSString *normalName, ...); - -@interface DKImage : NSObject - -/** - * A method takes an array of images return a image picker, the - * order of the images is just like the themes order in DKColorTable.txt - * file. - * - * @param images An array of images - * - * @return A DKImagePicker - */ -+ (DKImagePicker)pickerWithNames:(NSArray *)names; - -/** - * A method takes an array of images return a image picker, the - * order of the images is just like the themes order in DKColorTable.txt - * file. - * - * @param images An array of image names - * - * @return A DKImagePicker - */ -+ (DKImagePicker)pickerWithImages:(NSArray *)images; - -/** - * Returns a image picker return the same image no matter what the current - * theme version is - * - * @param name The name for image - * - * @return A DKImagePicker - */ -+ (DKImagePicker)imageNamed:(NSString *)name; - -/** - * Returns a image picker return night image when current theme version is - * DKThemeVersionNight, return normal image in other cases. - * - * @param normalImage Normal image - * @param nightImage Image returns when theme version is DKThemeVersionNight - * - * @return A DKImagePicker - */ -+ (DKImagePicker)pickerWithNormalImage:(UIImage *)normalImage nightImage:(UIImage *)nightImage; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Core/DKImage.m b/Pods/DKNightVersion/DKNightVersion/Core/DKImage.m deleted file mode 100644 index 1c06bb5..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Core/DKImage.m +++ /dev/null @@ -1,95 +0,0 @@ -// -// DKImage.m -// DKNightVersion -// -// Created by Draveness on 15/12/10. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import "DKImage.h" -#import "DKNightVersionManager.h" -#import "DKColorTable.h" - -@implementation DKImage - -DKImagePicker DKImagePickerWithNames(NSString *normalName, ...) { - NSArray *themes = [DKColorTable sharedColorTable].themes; - NSMutableArray *names = [[NSMutableArray alloc] initWithCapacity:themes.count]; - [names addObject:normalName]; - NSUInteger num_args = themes.count - 1; - va_list names_list; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wvarargs" - va_start(names_list, num_args); -#pragma clang diagnostic pop - for (NSUInteger i = 0; i < num_args; i++) { - NSString *name = va_arg(names_list, NSString *); - [names addObject:name]; - } - va_end(names_list); - - return [DKImage pickerWithNames:names]; -} - -DKImagePicker DKImagePickerWithImages(UIImage *normalImage, ...) { - NSArray *themes = [DKColorTable sharedColorTable].themes; - NSMutableArray *images = [[NSMutableArray alloc] initWithCapacity:themes.count]; - [images addObject:normalImage]; - NSUInteger num_args = themes.count - 1; - va_list images_list; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wvarargs" - va_start(images_list, num_args); -#pragma clang diagnostic pop - for (NSUInteger i = 0; i < num_args; i++) { - UIImage *image = va_arg(images_list, UIImage *); - [images addObject:image]; - } - va_end(images_list); - - return [DKImage pickerWithImages:images]; -} - -+ (DKImagePicker)pickerWithNormalImage:(UIImage *)normalImage nightImage:(UIImage *)nightImage { - NSParameterAssert(normalImage); - NSParameterAssert(nightImage); - return ^(DKThemeVersion *themeVersion) { - return [themeVersion isEqualToString:DKThemeVersionNight] ? nightImage : normalImage; - }; -} - -+ (DKImagePicker)pickerWithImage:(UIImage *)image { - return ^(DKThemeVersion *themeVersion) { - return image; - }; -} - -+ (DKImagePicker)imageNamed:(NSString *)name { - return [self pickerWithImage:[UIImage imageNamed:name]]; -} - -+ (DKImagePicker)pickerWithNames:(NSArray *)names { - DKColorTable *colorTable = [DKColorTable sharedColorTable]; - NSParameterAssert(names.count == colorTable.themes.count); - return ^(DKThemeVersion *themeVersion) { - NSUInteger index = [colorTable.themes indexOfObject:themeVersion]; - if (index >= colorTable.themes.count) { - return [UIImage imageNamed:names[[colorTable.themes indexOfObject:DKThemeVersionNormal]]]; - } - return [UIImage imageNamed:names[index]]; - }; -} - -+ (DKImagePicker)pickerWithImages:(NSArray *)images { - DKColorTable *colorTable = [DKColorTable sharedColorTable]; - NSParameterAssert(images.count == colorTable.themes.count); - return ^(DKThemeVersion *themeVersion) { - NSUInteger index = [colorTable.themes indexOfObject:themeVersion]; - if (index >= colorTable.themes.count) { - return images[[colorTable.themes indexOfObject:DKThemeVersionNormal]]; - } - return images[index]; - }; -} - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Core/DKNightVersionManager.h b/Pods/DKNightVersion/DKNightVersion/Core/DKNightVersionManager.h deleted file mode 100644 index 1d2d98c..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Core/DKNightVersionManager.h +++ /dev/null @@ -1,109 +0,0 @@ -// -// DKNightVersionManager.h -// DKNightVersionManager -// -// Created by Draveness on 4/14/15. -// Copyright (c) 2015 Draveness. All rights reserved. -// - -#import -#import "DKColor.h" -#import "DKImage.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - * DKThemeVersion is just a alias to string, use `- isEqualToString` to - * compare with each `DKThemeVersion` instead of symbol `==`. - */ -typedef NSString DKThemeVersion; - -/** - * DKThemeVersionNormal is just a const string @"NORMAL", but use `- isEqualToString:` - * to compare with another string. - */ -extern DKThemeVersion * const DKThemeVersionNormal; - -/** - * DKThemeVersionNight is just a const string @"NIGHT", but use `- isEqualToString:` - * to compare with another string. - */ -extern DKThemeVersion * const DKThemeVersionNight; - -/** - * This notification will post, every time you change current theme version - * of DKNightVersionManager glbal instance. - */ -extern NSString * const DKNightVersionThemeChangingNotificaiton; - -/** - * When change theme version, it will gives us a smooth animation. And this - * is the duration for this animation. - */ -extern CGFloat const DKNightVersionAnimationDuration; - -/** - * DKNightVersionManager is the core class for DKNightVersion, it manages all - * the different themes in the color table. Use `- sharedInstance` instead of - * `- init` to get an instance. - */ -@interface DKNightVersionManager : NSObject - -/** - * if `changeStatusBar` is set to `YES`, the status bar will change to `UIStatusBarStyleLightContent` when invoke `+ nightFalling` and `UIStatusBarStyleDefault` for `+ dawnComing`. if you would like to use `-[UIViewController preferredStatusBarStyle]`, set this value to `NO`. Default to `YES` - */ -@property (nonatomic, assign, getter=shouldChangeStatusBar) BOOL changeStatusBar; - -/** - * Current ThemeVersion, default is DKThemeVersionNormal, change it to change the global - * theme, this will post `DKNightVersionThemeChangingNotificaiton`, if you want to customize - * your theme you can observe this notification. - * - * Ex: - * - * ```objectivec - * DKNightVersionManager *manager = [DKNightVersionManager sharedManager]; - * manager.themeVersion = @"RED"; // DKThemeVersionNormal or DKThemeVersionNight - * ``` - * - */ -@property (nonatomic, strong) DKThemeVersion *themeVersion; - -/** - * Support keyboard type changes when swiching to DKThemeNight. If this value is YES, - * `keyboardType` for UITextField will change to `UIKeyboardAppearanceDark` only current theme - * version is DKThemeNight. Default is YES. - */ -@property (nonatomic, assign) BOOL supportsKeyboard; - -/** - * Return the shared night version manager instance - * - * @return singleton instance for DKNightVersionManager - */ -+ (DKNightVersionManager *)sharedManager; - -/** - * Night falling. When nightFalling is called, post `DKNightVersionThemeChangingNotificaiton`. - * You can setup customize with observing the notification. `themeVersion` of the manager will - * be set to `DKNightVersionNight`. This is a convinient method for switching theme the - * `DKThemeVersionNight`. - */ -- (void)nightFalling; - -/** - * Dawn coming. When dawnComing is called, post `DKNightVersionThemeChangingNotificaiton`. - * You can setup customize with observing the notification.`themeVersion` of the manager will - * be set to `DKNightVersionNormal`. This is a convinient method for switching theme the - * `DKThemeVersionNormal`. - */ -- (void)dawnComing; - -/** - * This method is deprecated, use `- [DKNightVersion sharedManager]` instead - */ -+ (DKNightVersionManager *)sharedNightVersionManager __deprecated_msg("use `- [DKNightVersion sharedManager]` instead"); - -@end - -NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/Pods/DKNightVersion/DKNightVersion/Core/DKNightVersionManager.m b/Pods/DKNightVersion/DKNightVersion/Core/DKNightVersionManager.m deleted file mode 100644 index f2f6be2..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Core/DKNightVersionManager.m +++ /dev/null @@ -1,77 +0,0 @@ -// -// DKNightVersionManager.m -// DKNightVersionManager -// -// Created by Draveness on 4/14/15. -// Copyright (c) 2015 Draveness. All rights reserved. -// - -#import "DKNightVersionManager.h" - -NSString * const DKThemeVersionNormal = @"NORMAL"; -NSString * const DKThemeVersionNight = @"NIGHT"; - -NSString * const DKNightVersionThemeChangingNotificaiton = @"DKNightVersionThemeChangingNotificaiton"; - -CGFloat const DKNightVersionAnimationDuration = 0.3; - -NSString * const DKNightVersionCurrentThemeVersionKey = @"com.dknightversion.manager.themeversion"; - -@interface DKNightVersionManager () - -@end - -@implementation DKNightVersionManager - -+ (DKNightVersionManager *)sharedManager { - static dispatch_once_t once; - static DKNightVersionManager *instance; - dispatch_once(&once, ^{ - instance = [self new]; - instance.changeStatusBar = YES; - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - DKThemeVersion *themeVersion = [userDefaults valueForKey:DKNightVersionCurrentThemeVersionKey]; - themeVersion = themeVersion ?: DKThemeVersionNormal; - instance.themeVersion = themeVersion; - instance.supportsKeyboard = YES; - }); - return instance; -} - -+ (DKNightVersionManager *)sharedNightVersionManager { - return [self sharedManager]; -} - -- (void)nightFalling { - self.themeVersion = DKThemeVersionNight; -} - -- (void)dawnComing { - self.themeVersion = DKThemeVersionNormal; -} - -- (void)setThemeVersion:(DKThemeVersion *)themeVersion { - if ([_themeVersion isEqualToString:themeVersion]) { - // if type does not change, don't execute code below to enhance performance. - return; - } - _themeVersion = themeVersion; - - // Save current theme version to user default - [[NSUserDefaults standardUserDefaults] setValue:themeVersion forKey:DKNightVersionCurrentThemeVersionKey]; - [[NSNotificationCenter defaultCenter] postNotificationName:DKNightVersionThemeChangingNotificaiton - object:nil]; - - if (self.shouldChangeStatusBar) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - if ([themeVersion isEqualToString:DKThemeVersionNight]) { - [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; - } else { - [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; - } -#pragma clang diagnostic pop - } -} - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Core/NSObject+Night.h b/Pods/DKNightVersion/DKNightVersion/Core/NSObject+Night.h deleted file mode 100644 index 8193de7..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Core/NSObject+Night.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSObject+Night.h -// DKNightVersion -// -// Created by Draveness on 15/11/7. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import -#import "DKNightVersionManager.h" - -@interface NSObject (Night) - -/** - * Default global DKNightVersionManager, this property gives us a more - * convinient way to access it. - */ -@property (nonatomic, strong, readonly) DKNightVersionManager *dk_manager; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Core/NSObject+Night.m b/Pods/DKNightVersion/DKNightVersion/Core/NSObject+Night.m deleted file mode 100644 index f3a92c8..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Core/NSObject+Night.m +++ /dev/null @@ -1,66 +0,0 @@ -// -// NSObject+Night.m -// DKNightVersion -// -// Created by Draveness on 15/11/7. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import "NSObject+Night.h" -#import "NSObject+DeallocBlock.h" -#import - -static void *DKViewDeallocHelperKey; - -@interface NSObject () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation NSObject (Night) - -- (NSMutableDictionary *)pickers { - NSMutableDictionary *pickers = objc_getAssociatedObject(self, @selector(pickers)); - if (!pickers) { - - @autoreleasepool { - // Need to removeObserver in dealloc - if (objc_getAssociatedObject(self, &DKViewDeallocHelperKey) == nil) { - __unsafe_unretained typeof(self) weakSelf = self; // NOTE: need to be __unsafe_unretained because __weak var will be reset to nil in dealloc - id deallocHelper = [self addDeallocBlock:^{ - [[NSNotificationCenter defaultCenter] removeObserver:weakSelf]; - }]; - objc_setAssociatedObject(self, &DKViewDeallocHelperKey, deallocHelper, OBJC_ASSOCIATION_ASSIGN); - } - } - - pickers = [[NSMutableDictionary alloc] init]; - objc_setAssociatedObject(self, @selector(pickers), pickers, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - [[NSNotificationCenter defaultCenter] removeObserver:self name:DKNightVersionThemeChangingNotificaiton object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(night_updateColor) name:DKNightVersionThemeChangingNotificaiton object:nil]; - } - return pickers; -} - -- (DKNightVersionManager *)dk_manager { - return [DKNightVersionManager sharedManager]; -} - -- (void)night_updateColor { - [self.pickers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull selector, DKColorPicker _Nonnull picker, BOOL * _Nonnull stop) { - SEL sel = NSSelectorFromString(selector); - id result = picker(self.dk_manager.themeVersion); - [UIView animateWithDuration:DKNightVersionAnimationDuration - animations:^{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [self performSelector:sel withObject:result]; -#pragma clang diagnostic pop - }]; - }]; -} - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/CoreAnimation/CALayer+Night.h b/Pods/DKNightVersion/DKNightVersion/CoreAnimation/CALayer+Night.h deleted file mode 100644 index 2158150..0000000 --- a/Pods/DKNightVersion/DKNightVersion/CoreAnimation/CALayer+Night.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// CALayer+Night.h -// DKNightVersion -// -// Created by Draveness on 16/1/29. -// Copyright © 2016年 DeltaX. All rights reserved. -// - -#import -#import "NSObject+Night.h" - -@interface CALayer (Night) - -@property (nonatomic, copy) DKColorPicker dk_shadowColorPicker; -@property (nonatomic, copy) DKColorPicker dk_borderColorPicker; -@property (nonatomic, copy) DKColorPicker dk_backgroundColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/CoreAnimation/CALayer+Night.m b/Pods/DKNightVersion/DKNightVersion/CoreAnimation/CALayer+Night.m deleted file mode 100644 index 2a69d32..0000000 --- a/Pods/DKNightVersion/DKNightVersion/CoreAnimation/CALayer+Night.m +++ /dev/null @@ -1,69 +0,0 @@ -// -// CALayer+Night.m -// DKNightVersion -// -// Created by Draveness on 16/1/29. -// Copyright © 2016年 DeltaX. All rights reserved. -// - -#import "CALayer+Night.h" -#import - -@interface CALayer () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation CALayer (Night) - -- (DKColorPicker)dk_shadowColorPicker { - return objc_getAssociatedObject(self, @selector(dk_shadowColorPicker)); -} - -- (void)setDk_shadowColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_shadowColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.shadowColor = picker(self.dk_manager.themeVersion).CGColor; - [self.pickers setValue:[picker copy] forKey:NSStringFromSelector(@selector(setShadowColor:))]; -} - -- (DKColorPicker)dk_borderColorPicker { - return objc_getAssociatedObject(self, @selector(dk_borderColorPicker)); -} - -- (void)setDk_borderColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_borderColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.borderColor = picker(self.dk_manager.themeVersion).CGColor; - [self.pickers setValue:[picker copy] forKey:NSStringFromSelector(@selector(setBorderColor:))]; -} - -- (DKColorPicker)dk_backgroundColorPicker { - return objc_getAssociatedObject(self, @selector(dk_backgroundColorPicker)); -} - -- (void)setDk_backgroundColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_backgroundColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.backgroundColor = picker(self.dk_manager.themeVersion).CGColor; - [self.pickers setValue:[picker copy] forKey:NSStringFromSelector(@selector(setBorderColor:))]; -} - -- (void)night_updateColor { - [self.pickers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull selector, DKColorPicker _Nonnull picker, BOOL * _Nonnull stop) { - CGColorRef result = picker(self.dk_manager.themeVersion).CGColor; - [UIView animateWithDuration:DKNightVersionAnimationDuration - animations:^{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - if ([selector isEqualToString:NSStringFromSelector(@selector(setShadowColor:))]) { - [self setShadowColor:result]; - } else if ([selector isEqualToString:NSStringFromSelector(@selector(setBorderColor:))]) { - [self setBorderColor:result]; - } else if ([selector isEqualToString:NSStringFromSelector(@selector(setBackgroundColor:)) ]) { - [self setBackgroundColor:result]; - } -#pragma clang diagnostic pop - }]; - }]; -} - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/CoreAnimation/CoreAnimation+Night.h b/Pods/DKNightVersion/DKNightVersion/CoreAnimation/CoreAnimation+Night.h deleted file mode 100644 index 03490f7..0000000 --- a/Pods/DKNightVersion/DKNightVersion/CoreAnimation/CoreAnimation+Night.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// CoreAnimation+Night.h -// DKNightVersion -// -// Created by Draveness on 16/4/1. -// Copyright © 2016年 DeltaX. All rights reserved. -// - -#ifndef CoreAnimation_Night_h -#define CoreAnimation_Night_h - -#import "CALayer+Night.h" - -#endif /* CoreAnimation_Night_h */ diff --git a/Pods/DKNightVersion/DKNightVersion/DKNightVersion.h b/Pods/DKNightVersion/DKNightVersion/DKNightVersion.h deleted file mode 100644 index 80f1161..0000000 --- a/Pods/DKNightVersion/DKNightVersion/DKNightVersion.h +++ /dev/null @@ -1,77 +0,0 @@ -// -// DKNightVersion.h -// DKNightVerision -// -// Created by Draveness on 4/14/15. -// Copyright (c) 2015 Draveness. All rights reserved. -// - -#import - -//! Project version number for DKNightVersion. -FOUNDATION_EXPORT double DKNightVersionVersionNumber; - -//! Project version string for DKNightVersion. -FOUNDATION_EXPORT const unsigned char DKNightVersionVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - -#ifndef _DKNIGHTVERSION_ -#define _DKNIGHTVERSION_ - -#import - -#import -#import -#import -#import - -#import - -#import - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import -#import - -#define _DKSetterWithPROPERTYerty(LOWERCASE) [NSString stringWithFormat:@"set%@:", [[[LOWERCASE substringToIndex:1] uppercaseString] stringByAppendingString:[LOWERCASE substringFromIndex:1]]] - -#define pickerify(KLASS, PROPERTY) interface \ - KLASS (Night) \ - @property (nonatomic, copy, setter = dk_set ## PROPERTY ## Picker:) DKColorPicker dk_ ## PROPERTY ## Picker; \ - @end \ - @interface \ - KLASS () \ - @property (nonatomic, strong) NSMutableDictionary *pickers; \ - @end \ - @implementation \ - KLASS (Night) \ - - (DKColorPicker)dk_ ## PROPERTY ## Picker { \ - return objc_getAssociatedObject(self, @selector(dk_ ## PROPERTY ## Picker)); \ - } \ - - (void)dk_set ## PROPERTY ## Picker:(DKColorPicker)picker { \ - objc_setAssociatedObject(self, @selector(dk_ ## PROPERTY ## Picker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); \ - [self setValue:picker(self.dk_manager.themeVersion) forKeyPath:@keypath(self, PROPERTY)];\ - [self.pickers setValue:[picker copy] forKey:_DKSetterWithPROPERTYerty(@#PROPERTY)]; \ - } \ - @end - - -#endif /* _DKNIGHTVERSION_ */ diff --git a/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/DKDeallocBlockExecutor.h b/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/DKDeallocBlockExecutor.h deleted file mode 100755 index 5c4f884..0000000 --- a/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/DKDeallocBlockExecutor.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// DeallocBlockExecutor.h -// DKNightVersion -// -// Created by nathanwhy on 16/2/24. -// Copyright © 2016年 Draveness. All rights reserved. -// - -#import - -@interface DKDeallocBlockExecutor : NSObject - -+ (instancetype)executorWithDeallocBlock:(void (^)())deallocBlock; - -@property (nonatomic, copy) void (^deallocBlock)(); - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/DKDeallocBlockExecutor.m b/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/DKDeallocBlockExecutor.m deleted file mode 100755 index 14a9147..0000000 --- a/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/DKDeallocBlockExecutor.m +++ /dev/null @@ -1,25 +0,0 @@ -// -// DeallocBlockExecutor.m -// DKNightVersion -// -// Created by nathanwhy on 16/2/24. -// Copyright © 2016年 Draveness. All rights reserved. -// - -#import "DKDeallocBlockExecutor.h" - -@implementation DKDeallocBlockExecutor - -+ (instancetype)executorWithDeallocBlock:(void (^)())deallocBlock { - DKDeallocBlockExecutor *o = [DKDeallocBlockExecutor new]; - o.deallocBlock = deallocBlock; - return o; -} - -- (void)dealloc { - if (self.deallocBlock) { - self.deallocBlock(); - self.deallocBlock = nil; - } -} -@end diff --git a/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/NSObject+DeallocBlock.h b/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/NSObject+DeallocBlock.h deleted file mode 100755 index 8730a4f..0000000 --- a/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/NSObject+DeallocBlock.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// NSObject+DeallocBlock.h -// DKNightVersion -// -// Created by nathanwhy on 16/2/24. -// Copyright © 2016年 Draveness. All rights reserved. -// - -#import - -@interface NSObject (DeallocBlock) - -- (id)addDeallocBlock:(void (^)())deallocBlock; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/NSObject+DeallocBlock.m b/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/NSObject+DeallocBlock.m deleted file mode 100755 index e2ffbb3..0000000 --- a/Pods/DKNightVersion/DKNightVersion/DeallocBlockExecutor/NSObject+DeallocBlock.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// NSObject+DeallocBlock.m -// DKNightVersion -// -// Created by nathanwhy on 16/2/24. -// Copyright © 2016年 Draveness. All rights reserved. -// - -#import "NSObject+DeallocBlock.h" -#import "DKDeallocBlockExecutor.h" -#import - -static void *kNSObject_DeallocBlocks; - -@implementation NSObject (DeallocBlock) - -- (id)addDeallocBlock:(void (^)())deallocBlock { - if (deallocBlock == nil) { - return nil; - } - - NSMutableArray *deallocBlocks = objc_getAssociatedObject(self, &kNSObject_DeallocBlocks); - if (deallocBlocks == nil) { - deallocBlocks = [NSMutableArray array]; - objc_setAssociatedObject(self, &kNSObject_DeallocBlocks, deallocBlocks, OBJC_ASSOCIATION_RETAIN); - } - // Check if the block is already existed - for (DKDeallocBlockExecutor *executor in deallocBlocks) { - if (executor.deallocBlock == deallocBlock) { - return nil; - } - } - - DKDeallocBlockExecutor *executor = [DKDeallocBlockExecutor executorWithDeallocBlock:deallocBlock]; - [deallocBlocks addObject:executor]; - return executor; -} - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Manual/UIButton+Night.h b/Pods/DKNightVersion/DKNightVersion/Manual/UIButton+Night.h deleted file mode 100644 index 762d451..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Manual/UIButton+Night.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// UIButton+Night.h -// DKNightVersion -// -// Created by Draveness on 15/12/9. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import -#import "NSObject+Night.h" - -@interface UIButton (Night) - -- (void)dk_setTitleColorPicker:(DKColorPicker)picker forState:(UIControlState)state; - -- (void)dk_setBackgroundImage:(DKImagePicker)picker forState:(UIControlState)state; - -- (void)dk_setImage:(DKImagePicker)picker forState:(UIControlState)state; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Manual/UIButton+Night.m b/Pods/DKNightVersion/DKNightVersion/Manual/UIButton+Night.m deleted file mode 100644 index 7a0ed47..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Manual/UIButton+Night.m +++ /dev/null @@ -1,89 +0,0 @@ -// -// UIButton+Night.m -// DKNightVersion -// -// Created by Draveness on 15/12/9. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import "UIButton+Night.h" -#import - -@interface UIButton () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UIButton (Night) - -- (void)dk_setTitleColorPicker:(DKColorPicker)picker forState:(UIControlState)state { - [self setTitleColor:picker(self.dk_manager.themeVersion) forState:state]; - NSString *key = [NSString stringWithFormat:@"%@", @(state)]; - NSMutableDictionary *dictionary = [self.pickers valueForKey:key]; - if (!dictionary) { - dictionary = [[NSMutableDictionary alloc] init]; - } - [dictionary setValue:[picker copy] forKey:NSStringFromSelector(@selector(setTitleColor:forState:))]; - [self.pickers setValue:dictionary forKey:key]; -} - -- (void)dk_setBackgroundImage:(DKImagePicker)picker forState:(UIControlState)state { - [self setBackgroundImage:picker(self.dk_manager.themeVersion) forState:state]; - NSString *key = [NSString stringWithFormat:@"%@", @(state)]; - NSMutableDictionary *dictionary = [self.pickers valueForKey:key]; - if (!dictionary) { - dictionary = [[NSMutableDictionary alloc] init]; - } - [dictionary setValue:[picker copy] forKey:NSStringFromSelector(@selector(setBackgroundImage:forState:))]; - [self.pickers setValue:dictionary forKey:key]; -} - -- (void)dk_setImage:(DKImagePicker)picker forState:(UIControlState)state { - [self setImage:picker(self.dk_manager.themeVersion) forState:state]; - NSString *key = [NSString stringWithFormat:@"%@", @(state)]; - NSMutableDictionary *dictionary = [self.pickers valueForKey:key]; - if (!dictionary) { - dictionary = [[NSMutableDictionary alloc] init]; - } - [dictionary setValue:[picker copy] forKey:NSStringFromSelector(@selector(setImage:forState:))]; - [self.pickers setValue:dictionary forKey:key]; -} - -- (void)night_updateColor { - [self.pickers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { - if ([obj isKindOfClass:[NSDictionary class]]) { - NSDictionary *dictionary = (NSDictionary *)obj; - [dictionary enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull selector, DKColorPicker _Nonnull picker, BOOL * _Nonnull stop) { - UIControlState state = [key integerValue]; - [UIView animateWithDuration:DKNightVersionAnimationDuration - animations:^{ - if ([selector isEqualToString:NSStringFromSelector(@selector(setTitleColor:forState:))]) { - UIColor *resultColor = picker(self.dk_manager.themeVersion); - [self setTitleColor:resultColor forState:state]; - } else if ([selector isEqualToString:NSStringFromSelector(@selector(setBackgroundImage:forState:))]) { - UIImage *resultImage = ((DKImagePicker)picker)(self.dk_manager.themeVersion); - [self setBackgroundImage:resultImage forState:state]; - } else if ([selector isEqualToString:NSStringFromSelector(@selector(setImage:forState:))]) { - UIImage *resultImage = ((DKImagePicker)picker)(self.dk_manager.themeVersion); - [self setImage:resultImage forState:state]; - } - }]; - }]; - } else { - SEL sel = NSSelectorFromString(key); - DKColorPicker picker = (DKColorPicker)obj; - UIColor *resultColor = picker(self.dk_manager.themeVersion); - [UIView animateWithDuration:DKNightVersionAnimationDuration - animations:^{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [self performSelector:sel withObject:resultColor]; -#pragma clang diagnostic pop - }]; - - } - }]; -} - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Manual/UIImageView+Night.h b/Pods/DKNightVersion/DKNightVersion/Manual/UIImageView+Night.h deleted file mode 100644 index bbcd8f7..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Manual/UIImageView+Night.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// UIImageView+Night.h -// DKNightVersion -// -// Created by Draveness on 15/12/10. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import -#import "DKNightVersionManager.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface UIImageView (Night) - -- (instancetype)dk_initWithImagePicker:(DKImagePicker)picker; - -@property (nullable, nonatomic, copy, setter = dk_setImagePicker:) DKImagePicker dk_imagePicker; - -@end - -NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/Pods/DKNightVersion/DKNightVersion/Manual/UIImageView+Night.m b/Pods/DKNightVersion/DKNightVersion/Manual/UIImageView+Night.m deleted file mode 100644 index 7c952b9..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Manual/UIImageView+Night.m +++ /dev/null @@ -1,38 +0,0 @@ -// -// UIImageView+Night.m -// DKNightVersion -// -// Created by Draveness on 15/12/10. -// Copyright © 2015年 DeltaX. All rights reserved. -// - -#import "UIImageView+Night.h" -#import "NSObject+Night.h" -#import - -@interface NSObject () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UIImageView (Night) - -- (instancetype)dk_initWithImagePicker:(DKImagePicker)picker { - UIImageView *imageView = [self initWithImage:picker(self.dk_manager.themeVersion)]; - imageView.dk_imagePicker = [picker copy]; - return imageView; -} - -- (DKImagePicker)dk_imagePicker { - return objc_getAssociatedObject(self, @selector(dk_imagePicker)); -} - -- (void)dk_setImagePicker:(DKImagePicker)picker { - objc_setAssociatedObject(self, @selector(dk_imagePicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.image = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setImage:"]; - -} - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Manual/UINavigationBar+Animation.h b/Pods/DKNightVersion/DKNightVersion/Manual/UINavigationBar+Animation.h deleted file mode 100644 index 492ffe9..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Manual/UINavigationBar+Animation.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// UINavigationBar+Animation.h -// DKNightVersion -// -// Created by Draveness on 15/5/4. -// Copyright (c) 2015年 DeltaX. All rights reserved. -// - -#import - -@interface UINavigationBar (Animation) - -- (void)animateNavigationBarToColor:(UIColor *)toColor - duration:(NSTimeInterval)duration; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Manual/UINavigationBar+Animation.m b/Pods/DKNightVersion/DKNightVersion/Manual/UINavigationBar+Animation.m deleted file mode 100644 index 546492e..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Manual/UINavigationBar+Animation.m +++ /dev/null @@ -1,69 +0,0 @@ -// -// UINavigationBar+Animation.m -// DKNightVersion -// -// Created by Draveness on 15/5/4. -// Copyright (c) 2015年 DeltaX. All rights reserved. -// - -#import "UINavigationBar+Animation.h" - -CGFloat const stepDuration = 0.01; - -@implementation UINavigationBar (Animation) - -- (void)animateNavigationBarToColor:(UIColor *)toColor duration:(NSTimeInterval)duration { - if (!self.barTintColor || !toColor) { - return; - } - UIColor *barDefaultColor = [UIColor colorWithRed:0.973 green:0.973 blue:0.973 alpha:1.0]; - - UIColor *barTintColor = self.barTintColor ? : barDefaultColor; - toColor = toColor ? : barDefaultColor; - NSUInteger steps = duration / stepDuration; - - CGFloat fromRed, fromGreen, fromBlue, fromAlpha; - CGFloat toRed, toGreen, toBlue, toAlpha; - - [barTintColor getRed:&fromRed green:&fromGreen blue:&fromBlue alpha:&fromAlpha]; - [toColor getRed:&toRed green:&toGreen blue:&toBlue alpha:&toAlpha]; - - CGFloat diffRed = toRed - fromRed; - CGFloat diffGreen = toGreen - fromGreen; - CGFloat diffBlue = toBlue - fromBlue; - CGFloat diffAlpha = toAlpha - fromAlpha; - - NSMutableArray *colorArray = [NSMutableArray array]; - - [colorArray addObject:barTintColor]; - - for (NSUInteger i = 0; i < steps - 1; ++i) { - CGFloat red = fromRed + diffRed / steps * (i + 1); - CGFloat green = fromGreen + diffGreen / steps * (i + 1); - CGFloat blue = fromBlue + diffBlue / steps * (i + 1); - CGFloat alpha = fromAlpha + diffAlpha / steps * (i + 1); - - UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; - [colorArray addObject:color]; - } - - [colorArray addObject:toColor]; - [self animateWithArray:colorArray]; -} - -- (void)animateWithArray:(NSMutableArray *)array { - NSUInteger counter = 0; - - for (UIColor *color in array) { - double delayInSeconds = stepDuration * counter++; - dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); - dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ - [UIView animateWithDuration:stepDuration animations:^{ - self.barTintColor = color; - }]; - }); - } -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Manual/UITextField+Keyboard.h b/Pods/DKNightVersion/DKNightVersion/Manual/UITextField+Keyboard.h deleted file mode 100644 index bd0eebc..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Manual/UITextField+Keyboard.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// UITextField+Keyboard.h -// DKNightVersion -// -// Created by Draveness on 16/4/11. -// Copyright © 2016年 Draveness. All rights reserved. -// - -#import - -@interface UITextField (Keyboard) - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/Manual/UITextField+Keyboard.m b/Pods/DKNightVersion/DKNightVersion/Manual/UITextField+Keyboard.m deleted file mode 100644 index 030df73..0000000 --- a/Pods/DKNightVersion/DKNightVersion/Manual/UITextField+Keyboard.m +++ /dev/null @@ -1,79 +0,0 @@ -// -// UITextField+Keyboard.m -// DKNightVersion -// -// Created by Draveness on 16/4/11. -// Copyright © 2016年 Draveness. All rights reserved. -// - -#import "UITextField+Keyboard.h" -#import "NSObject+Night.h" -#import - -@interface NSObject () - -- (void)night_updateColor; - -@end - - -@implementation UITextField (Keyboard) - -+ (void)load { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - Class class = [self class]; - - SEL originalSelector = @selector(init); - SEL swizzledSelector = @selector(dk_init); - - Method originalMethod = class_getInstanceMethod(class, originalSelector); - Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); - - BOOL didAddMethod = - class_addMethod(class, - originalSelector, - method_getImplementation(swizzledMethod), - method_getTypeEncoding(swizzledMethod)); - - if (didAddMethod) { - class_replaceMethod(class, - swizzledSelector, - method_getImplementation(originalMethod), - method_getTypeEncoding(originalMethod)); - } else { - method_exchangeImplementations(originalMethod, swizzledMethod); - } - }); - -} - -- (instancetype)dk_init { - UITextField *obj = [self dk_init]; - if (self.dk_manager.supportsKeyboard && [self.dk_manager.themeVersion isEqualToString:DKThemeVersionNight]) { -#ifdef __IPHONE_7_0 - obj.keyboardAppearance = UIKeyboardAppearanceDark; -#else - obj.keyboardAppearance = UIKeyboardAppearanceAlert; -#endif - } else { - obj.keyboardAppearance = UIKeyboardAppearanceDefault; - } - return obj; -} - -- (void)night_updateColor { - [super night_updateColor]; - if (self.dk_manager.supportsKeyboard && [self.dk_manager.themeVersion isEqualToString:DKThemeVersionNight]) { -#ifdef __IPHONE_7_0 - self.keyboardAppearance = UIKeyboardAppearanceDark; -#else - self.keyboardAppearance = UIKeyboardAppearanceAlert; -#endif - } else { - self.keyboardAppearance = UIKeyboardAppearanceDefault; - } -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIBarButtonItem+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UIBarButtonItem+Night.h deleted file mode 100644 index 592d79a..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIBarButtonItem+Night.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// UIBarButtonItem+Night.h -// UIBarButtonItem+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UIBarButtonItem (Night) - -@property (nonatomic, copy, setter = dk_setTintColorPicker:) DKColorPicker dk_tintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIBarButtonItem+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UIBarButtonItem+Night.m deleted file mode 100644 index f737c3e..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIBarButtonItem+Night.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// UIBarButtonItem+Night.m -// UIBarButtonItem+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UIBarButtonItem+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UIBarButtonItem () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UIBarButtonItem (Night) - - -- (DKColorPicker)dk_tintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_tintColorPicker)); -} - -- (void)dk_setTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_tintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.tintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIControl+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UIControl+Night.h deleted file mode 100644 index 50eab72..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIControl+Night.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// UIControl+Night.h -// UIControl+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UIControl (Night) - -@property (nonatomic, copy, setter = dk_setTintColorPicker:) DKColorPicker dk_tintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIControl+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UIControl+Night.m deleted file mode 100644 index 4afd88d..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIControl+Night.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// UIControl+Night.m -// UIControl+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UIControl+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UIControl () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UIControl (Night) - - -- (DKColorPicker)dk_tintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_tintColorPicker)); -} - -- (void)dk_setTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_tintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.tintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UILabel+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UILabel+Night.h deleted file mode 100644 index e3c41e3..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UILabel+Night.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// UILabel+Night.h -// UILabel+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UILabel (Night) - -@property (nonatomic, copy, setter = dk_setTextColorPicker:) DKColorPicker dk_textColorPicker; -@property (nonatomic, copy, setter = dk_setShadowColorPicker:) DKColorPicker dk_shadowColorPicker; -@property (nonatomic, copy, setter = dk_setHighlightedTextColorPicker:) DKColorPicker dk_highlightedTextColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UILabel+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UILabel+Night.m deleted file mode 100644 index 137deae..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UILabel+Night.m +++ /dev/null @@ -1,55 +0,0 @@ -// -// UILabel+Night.m -// UILabel+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UILabel+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UILabel () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UILabel (Night) - - -- (DKColorPicker)dk_textColorPicker { - return objc_getAssociatedObject(self, @selector(dk_textColorPicker)); -} - -- (void)dk_setTextColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_textColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.textColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setTextColor:"]; -} - -- (DKColorPicker)dk_shadowColorPicker { - return objc_getAssociatedObject(self, @selector(dk_shadowColorPicker)); -} - -- (void)dk_setShadowColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_shadowColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.shadowColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setShadowColor:"]; -} - -- (DKColorPicker)dk_highlightedTextColorPicker { - return objc_getAssociatedObject(self, @selector(dk_highlightedTextColorPicker)); -} - -- (void)dk_setHighlightedTextColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_highlightedTextColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.highlightedTextColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setHighlightedTextColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UINavigationBar+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UINavigationBar+Night.h deleted file mode 100644 index 9e25989..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UINavigationBar+Night.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// UINavigationBar+Night.h -// UINavigationBar+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UINavigationBar (Night) - -@property (nonatomic, copy, setter = dk_setBarTintColorPicker:) DKColorPicker dk_barTintColorPicker; -@property (nonatomic, copy, setter = dk_setTintColorPicker:) DKColorPicker dk_tintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UINavigationBar+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UINavigationBar+Night.m deleted file mode 100644 index 3820571..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UINavigationBar+Night.m +++ /dev/null @@ -1,45 +0,0 @@ -// -// UINavigationBar+Night.m -// UINavigationBar+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UINavigationBar+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UINavigationBar () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UINavigationBar (Night) - - -- (DKColorPicker)dk_barTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_barTintColorPicker)); -} - -- (void)dk_setBarTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_barTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.barTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setBarTintColor:"]; -} - -- (DKColorPicker)dk_tintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_tintColorPicker)); -} - -- (void)dk_setTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_tintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.tintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIPageControl+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UIPageControl+Night.h deleted file mode 100644 index 06fdb98..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIPageControl+Night.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIPageControl+Night.h -// UIPageControl+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UIPageControl (Night) - -@property (nonatomic, copy, setter = dk_setPageIndicatorTintColorPicker:) DKColorPicker dk_pageIndicatorTintColorPicker; -@property (nonatomic, copy, setter = dk_setCurrentPageIndicatorTintColorPicker:) DKColorPicker dk_currentPageIndicatorTintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIPageControl+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UIPageControl+Night.m deleted file mode 100644 index 5703475..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIPageControl+Night.m +++ /dev/null @@ -1,45 +0,0 @@ -// -// UIPageControl+Night.m -// UIPageControl+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UIPageControl+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UIPageControl () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UIPageControl (Night) - - -- (DKColorPicker)dk_pageIndicatorTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_pageIndicatorTintColorPicker)); -} - -- (void)dk_setPageIndicatorTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_pageIndicatorTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.pageIndicatorTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setPageIndicatorTintColor:"]; -} - -- (DKColorPicker)dk_currentPageIndicatorTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_currentPageIndicatorTintColorPicker)); -} - -- (void)dk_setCurrentPageIndicatorTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_currentPageIndicatorTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.currentPageIndicatorTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setCurrentPageIndicatorTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIProgressView+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UIProgressView+Night.h deleted file mode 100644 index 1376066..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIProgressView+Night.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIProgressView+Night.h -// UIProgressView+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UIProgressView (Night) - -@property (nonatomic, copy, setter = dk_setProgressTintColorPicker:) DKColorPicker dk_progressTintColorPicker; -@property (nonatomic, copy, setter = dk_setTrackTintColorPicker:) DKColorPicker dk_trackTintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIProgressView+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UIProgressView+Night.m deleted file mode 100644 index e7616b0..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIProgressView+Night.m +++ /dev/null @@ -1,45 +0,0 @@ -// -// UIProgressView+Night.m -// UIProgressView+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UIProgressView+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UIProgressView () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UIProgressView (Night) - - -- (DKColorPicker)dk_progressTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_progressTintColorPicker)); -} - -- (void)dk_setProgressTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_progressTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.progressTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setProgressTintColor:"]; -} - -- (DKColorPicker)dk_trackTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_trackTintColorPicker)); -} - -- (void)dk_setTrackTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_trackTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.trackTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setTrackTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UISearchBar+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UISearchBar+Night.h deleted file mode 100644 index 430cbb1..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UISearchBar+Night.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// UISearchBar+Night.h -// UISearchBar+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UISearchBar (Night) - -@property (nonatomic, copy, setter = dk_setBarTintColorPicker:) DKColorPicker dk_barTintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UISearchBar+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UISearchBar+Night.m deleted file mode 100644 index acf4cec..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UISearchBar+Night.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// UISearchBar+Night.m -// UISearchBar+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UISearchBar+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UISearchBar () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UISearchBar (Night) - - -- (DKColorPicker)dk_barTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_barTintColorPicker)); -} - -- (void)dk_setBarTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_barTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.barTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setBarTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UISlider+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UISlider+Night.h deleted file mode 100644 index 4f6b6fa..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UISlider+Night.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// UISlider+Night.h -// UISlider+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UISlider (Night) - -@property (nonatomic, copy, setter = dk_setMinimumTrackTintColorPicker:) DKColorPicker dk_minimumTrackTintColorPicker; -@property (nonatomic, copy, setter = dk_setMaximumTrackTintColorPicker:) DKColorPicker dk_maximumTrackTintColorPicker; -@property (nonatomic, copy, setter = dk_setThumbTintColorPicker:) DKColorPicker dk_thumbTintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UISlider+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UISlider+Night.m deleted file mode 100644 index 8cc75e5..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UISlider+Night.m +++ /dev/null @@ -1,55 +0,0 @@ -// -// UISlider+Night.m -// UISlider+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UISlider+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UISlider () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UISlider (Night) - - -- (DKColorPicker)dk_minimumTrackTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_minimumTrackTintColorPicker)); -} - -- (void)dk_setMinimumTrackTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_minimumTrackTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.minimumTrackTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setMinimumTrackTintColor:"]; -} - -- (DKColorPicker)dk_maximumTrackTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_maximumTrackTintColorPicker)); -} - -- (void)dk_setMaximumTrackTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_maximumTrackTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.maximumTrackTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setMaximumTrackTintColor:"]; -} - -- (DKColorPicker)dk_thumbTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_thumbTintColorPicker)); -} - -- (void)dk_setThumbTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_thumbTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.thumbTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setThumbTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UISwitch+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UISwitch+Night.h deleted file mode 100644 index a4d318b..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UISwitch+Night.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// UISwitch+Night.h -// UISwitch+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UISwitch (Night) - -@property (nonatomic, copy, setter = dk_setOnTintColorPicker:) DKColorPicker dk_onTintColorPicker; -@property (nonatomic, copy, setter = dk_setThumbTintColorPicker:) DKColorPicker dk_thumbTintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UISwitch+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UISwitch+Night.m deleted file mode 100644 index 54837f8..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UISwitch+Night.m +++ /dev/null @@ -1,45 +0,0 @@ -// -// UISwitch+Night.m -// UISwitch+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UISwitch+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UISwitch () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UISwitch (Night) - - -- (DKColorPicker)dk_onTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_onTintColorPicker)); -} - -- (void)dk_setOnTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_onTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.onTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setOnTintColor:"]; -} - -- (DKColorPicker)dk_thumbTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_thumbTintColorPicker)); -} - -- (void)dk_setThumbTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_thumbTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.thumbTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setThumbTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UITabBar+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UITabBar+Night.h deleted file mode 100644 index 53d26a4..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UITabBar+Night.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// UITabBar+Night.h -// UITabBar+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UITabBar (Night) - -@property (nonatomic, copy, setter = dk_setBarTintColorPicker:) DKColorPicker dk_barTintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UITabBar+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UITabBar+Night.m deleted file mode 100644 index 95b59be..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UITabBar+Night.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// UITabBar+Night.m -// UITabBar+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UITabBar+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UITabBar () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UITabBar (Night) - - -- (DKColorPicker)dk_barTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_barTintColorPicker)); -} - -- (void)dk_setBarTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_barTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.barTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setBarTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UITableView+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UITableView+Night.h deleted file mode 100644 index 5df13d5..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UITableView+Night.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// UITableView+Night.h -// UITableView+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UITableView (Night) - -@property (nonatomic, copy, setter = dk_setSeparatorColorPicker:) DKColorPicker dk_separatorColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UITableView+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UITableView+Night.m deleted file mode 100644 index 698eaed..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UITableView+Night.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// UITableView+Night.m -// UITableView+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UITableView+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UITableView () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UITableView (Night) - - -- (DKColorPicker)dk_separatorColorPicker { - return objc_getAssociatedObject(self, @selector(dk_separatorColorPicker)); -} - -- (void)dk_setSeparatorColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_separatorColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.separatorColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setSeparatorColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UITextField+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UITextField+Night.h deleted file mode 100644 index 45fd224..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UITextField+Night.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// UITextField+Night.h -// UITextField+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UITextField (Night) - -@property (nonatomic, copy, setter = dk_setTextColorPicker:) DKColorPicker dk_textColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UITextField+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UITextField+Night.m deleted file mode 100644 index fbaec5a..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UITextField+Night.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// UITextField+Night.m -// UITextField+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UITextField+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UITextField () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UITextField (Night) - - -- (DKColorPicker)dk_textColorPicker { - return objc_getAssociatedObject(self, @selector(dk_textColorPicker)); -} - -- (void)dk_setTextColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_textColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.textColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setTextColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UITextView+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UITextView+Night.h deleted file mode 100644 index a66f954..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UITextView+Night.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// UITextView+Night.h -// UITextView+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UITextView (Night) - -@property (nonatomic, copy, setter = dk_setTextColorPicker:) DKColorPicker dk_textColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UITextView+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UITextView+Night.m deleted file mode 100644 index 9badde4..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UITextView+Night.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// UITextView+Night.m -// UITextView+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UITextView+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UITextView () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UITextView (Night) - - -- (DKColorPicker)dk_textColorPicker { - return objc_getAssociatedObject(self, @selector(dk_textColorPicker)); -} - -- (void)dk_setTextColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_textColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.textColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setTextColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIToolbar+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UIToolbar+Night.h deleted file mode 100644 index 2a77e26..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIToolbar+Night.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// UIToolbar+Night.h -// UIToolbar+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UIToolbar (Night) - -@property (nonatomic, copy, setter = dk_setBarTintColorPicker:) DKColorPicker dk_barTintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIToolbar+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UIToolbar+Night.m deleted file mode 100644 index afb00f1..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIToolbar+Night.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// UIToolbar+Night.m -// UIToolbar+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UIToolbar+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UIToolbar () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UIToolbar (Night) - - -- (DKColorPicker)dk_barTintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_barTintColorPicker)); -} - -- (void)dk_setBarTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_barTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.barTintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setBarTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIView+Night.h b/Pods/DKNightVersion/DKNightVersion/UIKit/UIView+Night.h deleted file mode 100644 index f70ad73..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIView+Night.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIView+Night.h -// UIView+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import -#import "NSObject+Night.h" - -@interface UIView (Night) - -@property (nonatomic, copy, setter = dk_setBackgroundColorPicker:) DKColorPicker dk_backgroundColorPicker; -@property (nonatomic, copy, setter = dk_setTintColorPicker:) DKColorPicker dk_tintColorPicker; - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/UIKit/UIView+Night.m b/Pods/DKNightVersion/DKNightVersion/UIKit/UIView+Night.m deleted file mode 100644 index 84543aa..0000000 --- a/Pods/DKNightVersion/DKNightVersion/UIKit/UIView+Night.m +++ /dev/null @@ -1,45 +0,0 @@ -// -// UIView+Night.m -// UIView+Night -// -// Copyright (c) 2015 Draveness. All rights reserved. -// -// These files are generated by ruby script, if you want to modify code -// in this file, you are supposed to update the ruby code, run it and -// test it. And finally open a pull request. - -#import "UIView+Night.h" -#import "DKNightVersionManager.h" -#import - -@interface UIView () - -@property (nonatomic, strong) NSMutableDictionary *pickers; - -@end - -@implementation UIView (Night) - - -- (DKColorPicker)dk_backgroundColorPicker { - return objc_getAssociatedObject(self, @selector(dk_backgroundColorPicker)); -} - -- (void)dk_setBackgroundColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_backgroundColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.backgroundColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setBackgroundColor:"]; -} - -- (DKColorPicker)dk_tintColorPicker { - return objc_getAssociatedObject(self, @selector(dk_tintColorPicker)); -} - -- (void)dk_setTintColorPicker:(DKColorPicker)picker { - objc_setAssociatedObject(self, @selector(dk_tintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); - self.tintColor = picker(self.dk_manager.themeVersion); - [self.pickers setValue:[picker copy] forKey:@"setTintColor:"]; -} - - -@end diff --git a/Pods/DKNightVersion/DKNightVersion/extobjc/EXTKeyPathCoding.h b/Pods/DKNightVersion/DKNightVersion/extobjc/EXTKeyPathCoding.h deleted file mode 100644 index f34dc4a..0000000 --- a/Pods/DKNightVersion/DKNightVersion/extobjc/EXTKeyPathCoding.h +++ /dev/null @@ -1,68 +0,0 @@ -// -// EXTKeyPathCoding.h -// extobjc -// -// Created by Justin Spahr-Summers on 19.06.12. -// Copyright (C) 2012 Justin Spahr-Summers. -// Released under the MIT license. -// - -#import -#import "metamacros.h" - -/** - * \@keypath allows compile-time verification of key paths. Given a real object - * receiver and key path: - * - * @code - -NSString *UTF8StringPath = @keypath(str.lowercaseString.UTF8String); -// => @"lowercaseString.UTF8String" - -NSString *versionPath = @keypath(NSObject, version); -// => @"version" - -NSString *lowercaseStringPath = @keypath(NSString.new, lowercaseString); -// => @"lowercaseString" - - * @endcode - * - * ... the macro returns an \c NSString containing all but the first path - * component or argument (e.g., @"lowercaseString.UTF8String", @"version"). - * - * In addition to simply creating a key path, this macro ensures that the key - * path is valid at compile-time (causing a syntax error if not), and supports - * refactoring, such that changing the name of the property will also update any - * uses of \@keypath. - */ -#define keypath(...) \ - metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__)) - -#define keypath1(PATH) \ - (((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1)) - -#define keypath2(OBJ, PATH) \ - (((void)(NO && ((void)OBJ.PATH, NO)), # PATH)) - -/** - * \@collectionKeypath allows compile-time verification of key paths across collections NSArray/NSSet etc. Given a real object - * receiver, collection object receiver and related keypaths: - * - * @code - - NSString *employessFirstNamePath = @collectionKeypath(department.employees, Employee.new, firstName) - // => @"employees.firstName" - - NSString *employessFirstNamePath = @collectionKeypath(Department.new, employees, Employee.new, firstName) - // => @"employees.firstName" - - * @endcode - * - */ -#define collectionKeypath(...) \ - metamacro_if_eq(3, metamacro_argcount(__VA_ARGS__))(collectionKeypath3(__VA_ARGS__))(collectionKeypath4(__VA_ARGS__)) - -#define collectionKeypath3(PATH, COLLECTION_OBJECT, COLLECTION_PATH) ([[NSString stringWithFormat:@"%s.%s",keypath(PATH), keypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) - -#define collectionKeypath4(OBJ, PATH, COLLECTION_OBJECT, COLLECTION_PATH) ([[NSString stringWithFormat:@"%s.%s",keypath(OBJ, PATH), keypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) - diff --git a/Pods/DKNightVersion/DKNightVersion/extobjc/metamacros.h b/Pods/DKNightVersion/DKNightVersion/extobjc/metamacros.h deleted file mode 100644 index 77a77b5..0000000 --- a/Pods/DKNightVersion/DKNightVersion/extobjc/metamacros.h +++ /dev/null @@ -1,666 +0,0 @@ -/** - * Macros for metaprogramming - * ExtendedC - * - * Copyright (C) 2012 Justin Spahr-Summers - * Released under the MIT license - */ - -#ifndef EXTC_METAMACROS_H -#define EXTC_METAMACROS_H - -/** - * Executes one or more expressions (which may have a void type, such as a call - * to a function that returns no value) and always returns true. - */ -#define metamacro_exprify(...) \ - ((__VA_ARGS__), true) - -/** - * Returns a string representation of VALUE after full macro expansion. - */ -#define metamacro_stringify(VALUE) \ - metamacro_stringify_(VALUE) - -/** - * Returns A and B concatenated after full macro expansion. - */ -#define metamacro_concat(A, B) \ - metamacro_concat_(A, B) - -/** - * Returns the Nth variadic argument (starting from zero). At least - * N + 1 variadic arguments must be given. N must be between zero and twenty, - * inclusive. - */ -#define metamacro_at(N, ...) \ - metamacro_concat(metamacro_at, N)(__VA_ARGS__) - -/** - * Returns the number of arguments (up to twenty) provided to the macro. At - * least one argument must be provided. - * - * Inspired by P99: http://p99.gforge.inria.fr - */ -#define metamacro_argcount(...) \ - metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) - -/** - * Identical to #metamacro_foreach_cxt, except that no CONTEXT argument is - * given. Only the index and current argument will thus be passed to MACRO. - */ -#define metamacro_foreach(MACRO, SEP, ...) \ - metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__) - -/** - * For each consecutive variadic argument (up to twenty), MACRO is passed the - * zero-based index of the current argument, CONTEXT, and then the argument - * itself. The results of adjoining invocations of MACRO are then separated by - * SEP. - * - * Inspired by P99: http://p99.gforge.inria.fr - */ -#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \ - metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) - -/** - * Identical to #metamacro_foreach_cxt. This can be used when the former would - * fail due to recursive macro expansion. - */ -#define metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...) \ - metamacro_concat(metamacro_foreach_cxt_recursive, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) - -/** - * In consecutive order, appends each variadic argument (up to twenty) onto - * BASE. The resulting concatenations are then separated by SEP. - * - * This is primarily useful to manipulate a list of macro invocations into instead - * invoking a different, possibly related macro. - */ -#define metamacro_foreach_concat(BASE, SEP, ...) \ - metamacro_foreach_cxt(metamacro_foreach_concat_iter, SEP, BASE, __VA_ARGS__) - -/** - * Iterates COUNT times, each time invoking MACRO with the current index - * (starting at zero) and CONTEXT. The results of adjoining invocations of MACRO - * are then separated by SEP. - * - * COUNT must be an integer between zero and twenty, inclusive. - */ -#define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) \ - metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT) - -/** - * Returns the first argument given. At least one argument must be provided. - * - * This is useful when implementing a variadic macro, where you may have only - * one variadic argument, but no way to retrieve it (for example, because \c ... - * always needs to match at least one argument). - * - * @code - -#define varmacro(...) \ - metamacro_head(__VA_ARGS__) - - * @endcode - */ -#define metamacro_head(...) \ - metamacro_head_(__VA_ARGS__, 0) - -/** - * Returns every argument except the first. At least two arguments must be - * provided. - */ -#define metamacro_tail(...) \ - metamacro_tail_(__VA_ARGS__) - -/** - * Returns the first N (up to twenty) variadic arguments as a new argument list. - * At least N variadic arguments must be provided. - */ -#define metamacro_take(N, ...) \ - metamacro_concat(metamacro_take, N)(__VA_ARGS__) - -/** - * Removes the first N (up to twenty) variadic arguments from the given argument - * list. At least N variadic arguments must be provided. - */ -#define metamacro_drop(N, ...) \ - metamacro_concat(metamacro_drop, N)(__VA_ARGS__) - -/** - * Decrements VAL, which must be a number between zero and twenty, inclusive. - * - * This is primarily useful when dealing with indexes and counts in - * metaprogramming. - */ -#define metamacro_dec(VAL) \ - metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) - -/** - * Increments VAL, which must be a number between zero and twenty, inclusive. - * - * This is primarily useful when dealing with indexes and counts in - * metaprogramming. - */ -#define metamacro_inc(VAL) \ - metamacro_at(VAL, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) - -/** - * If A is equal to B, the next argument list is expanded; otherwise, the - * argument list after that is expanded. A and B must be numbers between zero - * and twenty, inclusive. Additionally, B must be greater than or equal to A. - * - * @code - -// expands to true -metamacro_if_eq(0, 0)(true)(false) - -// expands to false -metamacro_if_eq(0, 1)(true)(false) - - * @endcode - * - * This is primarily useful when dealing with indexes and counts in - * metaprogramming. - */ -#define metamacro_if_eq(A, B) \ - metamacro_concat(metamacro_if_eq, A)(B) - -/** - * Identical to #metamacro_if_eq. This can be used when the former would fail - * due to recursive macro expansion. - */ -#define metamacro_if_eq_recursive(A, B) \ - metamacro_concat(metamacro_if_eq_recursive, A)(B) - -/** - * Returns 1 if N is an even number, or 0 otherwise. N must be between zero and - * twenty, inclusive. - * - * For the purposes of this test, zero is considered even. - */ -#define metamacro_is_even(N) \ - metamacro_at(N, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1) - -/** - * Returns the logical NOT of B, which must be the number zero or one. - */ -#define metamacro_not(B) \ - metamacro_at(B, 1, 0) - -// IMPLEMENTATION DETAILS FOLLOW! -// Do not write code that depends on anything below this line. -#define metamacro_stringify_(VALUE) # VALUE -#define metamacro_concat_(A, B) A ## B -#define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG) -#define metamacro_head_(FIRST, ...) FIRST -#define metamacro_tail_(FIRST, ...) __VA_ARGS__ -#define metamacro_consume_(...) -#define metamacro_expand_(...) __VA_ARGS__ - -// implemented from scratch so that metamacro_concat() doesn't end up nesting -#define metamacro_foreach_concat_iter(INDEX, BASE, ARG) metamacro_foreach_concat_iter_(BASE, ARG) -#define metamacro_foreach_concat_iter_(BASE, ARG) BASE ## ARG - -// metamacro_at expansions -#define metamacro_at0(...) metamacro_head(__VA_ARGS__) -#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__) - -// metamacro_foreach_cxt expansions -#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT) -#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) - -#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ - metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \ - SEP \ - MACRO(1, CONTEXT, _1) - -#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ - metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ - SEP \ - MACRO(2, CONTEXT, _2) - -#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ - metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ - SEP \ - MACRO(3, CONTEXT, _3) - -#define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ - metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ - SEP \ - MACRO(4, CONTEXT, _4) - -#define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ - metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ - SEP \ - MACRO(5, CONTEXT, _5) - -#define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ - metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ - SEP \ - MACRO(6, CONTEXT, _6) - -#define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ - metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ - SEP \ - MACRO(7, CONTEXT, _7) - -#define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ - metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ - SEP \ - MACRO(8, CONTEXT, _8) - -#define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ - metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ - SEP \ - MACRO(9, CONTEXT, _9) - -#define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ - metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ - SEP \ - MACRO(10, CONTEXT, _10) - -#define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ - metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ - SEP \ - MACRO(11, CONTEXT, _11) - -#define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ - metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ - SEP \ - MACRO(12, CONTEXT, _12) - -#define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ - metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ - SEP \ - MACRO(13, CONTEXT, _13) - -#define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ - metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ - SEP \ - MACRO(14, CONTEXT, _14) - -#define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ - metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ - SEP \ - MACRO(15, CONTEXT, _15) - -#define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ - metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ - SEP \ - MACRO(16, CONTEXT, _16) - -#define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ - metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ - SEP \ - MACRO(17, CONTEXT, _17) - -#define metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ - metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ - SEP \ - MACRO(18, CONTEXT, _18) - -#define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ - metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ - SEP \ - MACRO(19, CONTEXT, _19) - -// metamacro_foreach_cxt_recursive expansions -#define metamacro_foreach_cxt_recursive0(MACRO, SEP, CONTEXT) -#define metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) - -#define metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ - metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) \ - SEP \ - MACRO(1, CONTEXT, _1) - -#define metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ - metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ - SEP \ - MACRO(2, CONTEXT, _2) - -#define metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ - metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ - SEP \ - MACRO(3, CONTEXT, _3) - -#define metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ - metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ - SEP \ - MACRO(4, CONTEXT, _4) - -#define metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ - metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ - SEP \ - MACRO(5, CONTEXT, _5) - -#define metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ - metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ - SEP \ - MACRO(6, CONTEXT, _6) - -#define metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ - metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ - SEP \ - MACRO(7, CONTEXT, _7) - -#define metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ - metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ - SEP \ - MACRO(8, CONTEXT, _8) - -#define metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ - metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ - SEP \ - MACRO(9, CONTEXT, _9) - -#define metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ - metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ - SEP \ - MACRO(10, CONTEXT, _10) - -#define metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ - metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ - SEP \ - MACRO(11, CONTEXT, _11) - -#define metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ - metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ - SEP \ - MACRO(12, CONTEXT, _12) - -#define metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ - metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ - SEP \ - MACRO(13, CONTEXT, _13) - -#define metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ - metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ - SEP \ - MACRO(14, CONTEXT, _14) - -#define metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ - metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ - SEP \ - MACRO(15, CONTEXT, _15) - -#define metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ - metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ - SEP \ - MACRO(16, CONTEXT, _16) - -#define metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ - metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ - SEP \ - MACRO(17, CONTEXT, _17) - -#define metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ - metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ - SEP \ - MACRO(18, CONTEXT, _18) - -#define metamacro_foreach_cxt_recursive20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ - metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ - SEP \ - MACRO(19, CONTEXT, _19) - -// metamacro_for_cxt expansions -#define metamacro_for_cxt0(MACRO, SEP, CONTEXT) -#define metamacro_for_cxt1(MACRO, SEP, CONTEXT) MACRO(0, CONTEXT) - -#define metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt1(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(1, CONTEXT) - -#define metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(2, CONTEXT) - -#define metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(3, CONTEXT) - -#define metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(4, CONTEXT) - -#define metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(5, CONTEXT) - -#define metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(6, CONTEXT) - -#define metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(7, CONTEXT) - -#define metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(8, CONTEXT) - -#define metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(9, CONTEXT) - -#define metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(10, CONTEXT) - -#define metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(11, CONTEXT) - -#define metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(12, CONTEXT) - -#define metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(13, CONTEXT) - -#define metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(14, CONTEXT) - -#define metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(15, CONTEXT) - -#define metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(16, CONTEXT) - -#define metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(17, CONTEXT) - -#define metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(18, CONTEXT) - -#define metamacro_for_cxt20(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(19, CONTEXT) - -// metamacro_if_eq expansions -#define metamacro_if_eq0(VALUE) \ - metamacro_concat(metamacro_if_eq0_, VALUE) - -#define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_ -#define metamacro_if_eq0_1(...) metamacro_expand_ -#define metamacro_if_eq0_2(...) metamacro_expand_ -#define metamacro_if_eq0_3(...) metamacro_expand_ -#define metamacro_if_eq0_4(...) metamacro_expand_ -#define metamacro_if_eq0_5(...) metamacro_expand_ -#define metamacro_if_eq0_6(...) metamacro_expand_ -#define metamacro_if_eq0_7(...) metamacro_expand_ -#define metamacro_if_eq0_8(...) metamacro_expand_ -#define metamacro_if_eq0_9(...) metamacro_expand_ -#define metamacro_if_eq0_10(...) metamacro_expand_ -#define metamacro_if_eq0_11(...) metamacro_expand_ -#define metamacro_if_eq0_12(...) metamacro_expand_ -#define metamacro_if_eq0_13(...) metamacro_expand_ -#define metamacro_if_eq0_14(...) metamacro_expand_ -#define metamacro_if_eq0_15(...) metamacro_expand_ -#define metamacro_if_eq0_16(...) metamacro_expand_ -#define metamacro_if_eq0_17(...) metamacro_expand_ -#define metamacro_if_eq0_18(...) metamacro_expand_ -#define metamacro_if_eq0_19(...) metamacro_expand_ -#define metamacro_if_eq0_20(...) metamacro_expand_ - -#define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE)) -#define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE)) -#define metamacro_if_eq3(VALUE) metamacro_if_eq2(metamacro_dec(VALUE)) -#define metamacro_if_eq4(VALUE) metamacro_if_eq3(metamacro_dec(VALUE)) -#define metamacro_if_eq5(VALUE) metamacro_if_eq4(metamacro_dec(VALUE)) -#define metamacro_if_eq6(VALUE) metamacro_if_eq5(metamacro_dec(VALUE)) -#define metamacro_if_eq7(VALUE) metamacro_if_eq6(metamacro_dec(VALUE)) -#define metamacro_if_eq8(VALUE) metamacro_if_eq7(metamacro_dec(VALUE)) -#define metamacro_if_eq9(VALUE) metamacro_if_eq8(metamacro_dec(VALUE)) -#define metamacro_if_eq10(VALUE) metamacro_if_eq9(metamacro_dec(VALUE)) -#define metamacro_if_eq11(VALUE) metamacro_if_eq10(metamacro_dec(VALUE)) -#define metamacro_if_eq12(VALUE) metamacro_if_eq11(metamacro_dec(VALUE)) -#define metamacro_if_eq13(VALUE) metamacro_if_eq12(metamacro_dec(VALUE)) -#define metamacro_if_eq14(VALUE) metamacro_if_eq13(metamacro_dec(VALUE)) -#define metamacro_if_eq15(VALUE) metamacro_if_eq14(metamacro_dec(VALUE)) -#define metamacro_if_eq16(VALUE) metamacro_if_eq15(metamacro_dec(VALUE)) -#define metamacro_if_eq17(VALUE) metamacro_if_eq16(metamacro_dec(VALUE)) -#define metamacro_if_eq18(VALUE) metamacro_if_eq17(metamacro_dec(VALUE)) -#define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE)) -#define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE)) - -// metamacro_if_eq_recursive expansions -#define metamacro_if_eq_recursive0(VALUE) \ - metamacro_concat(metamacro_if_eq_recursive0_, VALUE) - -#define metamacro_if_eq_recursive0_0(...) __VA_ARGS__ metamacro_consume_ -#define metamacro_if_eq_recursive0_1(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_2(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_3(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_4(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_5(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_6(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_7(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_8(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_9(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_10(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_11(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_12(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_13(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_14(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_15(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_16(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_17(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_18(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_19(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_20(...) metamacro_expand_ - -#define metamacro_if_eq_recursive1(VALUE) metamacro_if_eq_recursive0(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive2(VALUE) metamacro_if_eq_recursive1(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive3(VALUE) metamacro_if_eq_recursive2(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive4(VALUE) metamacro_if_eq_recursive3(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive5(VALUE) metamacro_if_eq_recursive4(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive6(VALUE) metamacro_if_eq_recursive5(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive7(VALUE) metamacro_if_eq_recursive6(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive8(VALUE) metamacro_if_eq_recursive7(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive9(VALUE) metamacro_if_eq_recursive8(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive10(VALUE) metamacro_if_eq_recursive9(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive11(VALUE) metamacro_if_eq_recursive10(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive12(VALUE) metamacro_if_eq_recursive11(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive13(VALUE) metamacro_if_eq_recursive12(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive14(VALUE) metamacro_if_eq_recursive13(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive15(VALUE) metamacro_if_eq_recursive14(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive16(VALUE) metamacro_if_eq_recursive15(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive17(VALUE) metamacro_if_eq_recursive16(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive18(VALUE) metamacro_if_eq_recursive17(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive19(VALUE) metamacro_if_eq_recursive18(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive20(VALUE) metamacro_if_eq_recursive19(metamacro_dec(VALUE)) - -// metamacro_take expansions -#define metamacro_take0(...) -#define metamacro_take1(...) metamacro_head(__VA_ARGS__) -#define metamacro_take2(...) metamacro_head(__VA_ARGS__), metamacro_take1(metamacro_tail(__VA_ARGS__)) -#define metamacro_take3(...) metamacro_head(__VA_ARGS__), metamacro_take2(metamacro_tail(__VA_ARGS__)) -#define metamacro_take4(...) metamacro_head(__VA_ARGS__), metamacro_take3(metamacro_tail(__VA_ARGS__)) -#define metamacro_take5(...) metamacro_head(__VA_ARGS__), metamacro_take4(metamacro_tail(__VA_ARGS__)) -#define metamacro_take6(...) metamacro_head(__VA_ARGS__), metamacro_take5(metamacro_tail(__VA_ARGS__)) -#define metamacro_take7(...) metamacro_head(__VA_ARGS__), metamacro_take6(metamacro_tail(__VA_ARGS__)) -#define metamacro_take8(...) metamacro_head(__VA_ARGS__), metamacro_take7(metamacro_tail(__VA_ARGS__)) -#define metamacro_take9(...) metamacro_head(__VA_ARGS__), metamacro_take8(metamacro_tail(__VA_ARGS__)) -#define metamacro_take10(...) metamacro_head(__VA_ARGS__), metamacro_take9(metamacro_tail(__VA_ARGS__)) -#define metamacro_take11(...) metamacro_head(__VA_ARGS__), metamacro_take10(metamacro_tail(__VA_ARGS__)) -#define metamacro_take12(...) metamacro_head(__VA_ARGS__), metamacro_take11(metamacro_tail(__VA_ARGS__)) -#define metamacro_take13(...) metamacro_head(__VA_ARGS__), metamacro_take12(metamacro_tail(__VA_ARGS__)) -#define metamacro_take14(...) metamacro_head(__VA_ARGS__), metamacro_take13(metamacro_tail(__VA_ARGS__)) -#define metamacro_take15(...) metamacro_head(__VA_ARGS__), metamacro_take14(metamacro_tail(__VA_ARGS__)) -#define metamacro_take16(...) metamacro_head(__VA_ARGS__), metamacro_take15(metamacro_tail(__VA_ARGS__)) -#define metamacro_take17(...) metamacro_head(__VA_ARGS__), metamacro_take16(metamacro_tail(__VA_ARGS__)) -#define metamacro_take18(...) metamacro_head(__VA_ARGS__), metamacro_take17(metamacro_tail(__VA_ARGS__)) -#define metamacro_take19(...) metamacro_head(__VA_ARGS__), metamacro_take18(metamacro_tail(__VA_ARGS__)) -#define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__)) - -// metamacro_drop expansions -#define metamacro_drop0(...) __VA_ARGS__ -#define metamacro_drop1(...) metamacro_tail(__VA_ARGS__) -#define metamacro_drop2(...) metamacro_drop1(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop3(...) metamacro_drop2(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop4(...) metamacro_drop3(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop5(...) metamacro_drop4(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop6(...) metamacro_drop5(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop7(...) metamacro_drop6(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop8(...) metamacro_drop7(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop9(...) metamacro_drop8(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop10(...) metamacro_drop9(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop11(...) metamacro_drop10(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop12(...) metamacro_drop11(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop13(...) metamacro_drop12(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop14(...) metamacro_drop13(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop15(...) metamacro_drop14(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop16(...) metamacro_drop15(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop17(...) metamacro_drop16(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop18(...) metamacro_drop17(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop19(...) metamacro_drop18(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__)) - -#endif diff --git a/Pods/DKNightVersion/LICENSE b/Pods/DKNightVersion/LICENSE deleted file mode 100644 index 1c12750..0000000 --- a/Pods/DKNightVersion/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Draveness - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/Pods/DKNightVersion/README.md b/Pods/DKNightVersion/README.md deleted file mode 100644 index 0df97ee..0000000 --- a/Pods/DKNightVersion/README.md +++ /dev/null @@ -1,343 +0,0 @@ -![](./images/Banner.png) - -

- - - - - - -

- -- [x] Easily integrate and high performance -- [x] Providing UIKit and CoreAnimation category -- [x] Read color customization from file -- [x] Support different themes -- [x] Generate picker for other lib with one line macro - -# Demo - -![](./images/DKNightVersion.gif) - ----- - -+ [Installation with CocoaPods](#Installation-with-CocoaPods) - + [Podfile](#podfile) - + [Import](#import) -+ [Usage](#usage) -+ [Advanced Usage](#advanced-usage) - + [DKNightVersionManger](#dknightversionmanager) - + [Change Theme](#change-theme) - + [Post Notification](#post-notification) - + [DKColorPicker](#dkColorpicker) - + [DKColorTable](#dkcolortable) - + [pickerify](#pickerify) - + [Create temporary DKColorPicker](#create-temporary-dkcolorpicker) - + [DKImagePicker](#dkimagepicker) - - -# How To Get Started - -DKNightVersion supports multiple methods for installing the library in a project. - -## Installation with CocoaPods - -[CocoaPods](https://cocoapods.org/) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like DKNightVersion in your projects. See the [Get Started section](https://cocoapods.org/#get_started) for more details. - -### Podfile - -To integrate DKNightVersion into your Xcode project using CocoaPods, specify it in your `Podfile`: - -```shell -pod "DKNightVersion", "~> 2.2.0" -``` - -Then, run the following command: - -```shell -$ pod install -``` - - -### Import - -Import DKNightVersion header file - -```objectivec -# import -``` - -## Usage - -Checkout `DKColorTable.txt` file in your project, which locates in `Pods/DKNightVersion/Resources/DKNightVersion.txt` - -``` -NORMAL NIGHT -# ffffff #343434 BG -# aaaaaa #313131 SEP -``` - -And then, set color picker like this - -```objectivec -self.view.dk_backgroundColorPicker = DKColorPickerWithKey(BG); -``` - -After the current theme version change to `DKThemeVersionNight`, the view background color will switch to `#343434`. - -```objectivec -[DKNightVersionManager nightFalling]; -``` - -or - -```objectivec -DKNightVersionManager *manager = [DKNightVersionManager sharedInstance]; -manager.themeVersion = DKThemeVersionNormal; -``` - -## Advanced Usage - -There are two approaches you can use to integrate night mode to your iOS App. - -### DKNightVersionManager - -The latest version for DKNightVersion add a readonly `dk_manager` property for `NSObject` returns the `DKNightVersionManager` singleton. - -#### Change Theme - -You can call `nightFalling` or `dawnComing` to switch current theme version to `DKThemeVersionNight` or `DKThemeVersionNormal`. - -```objectivec -[self.dk_manager dawnComing]; -[self.dk_manager nightFalling]; -``` - -Modify `themeVersion` property to directly switch theme version. - -```objectivec -self.dk_manager.themeVersion = DKThemeVersionNormal; -self.dk_manager.themeVersion = DKThemeVersionNight; -// if there is a RED column in DKColorTable.txt (default) or in -// other `file` if you customize `file` property for `DKColorTable` -self.dk_manager.themeVersion = @"RED"; -``` - -#### Post Notification - -Every time the current theme version changes, `DKNightVersionManager` will posts a `DKNightVersionThemeChangingNotificaiton`. If you wanna to do some customization, you can observe this notification and react with proper actions. - -### DKColorPicker - -`DKColorPicker` is the core of DKNightVersion. And this lib adds dk_colorPicker to every UIKit and Core Animation components. Ex: - -```objectivec -@property (nonatomic, copy, setter = dk_setBackgroundColorPicker:) DKColorPicker dk_backgroundColorPicker; -@property (nonatomic, copy, setter = dk_setTintColorPicker:) DKColorPicker dk_tintColorPicker; -``` - -DKColorPicker is defined in `DKColor.h` file, receives a `DKThemeVersion` as parameter and return a `UIColor`. - -```objectivec -typedef UIColor *(^DKColorPicker)(DKThemeVersion *themeVersion); -``` - -+ Use `DKColorPickerWithKey(key)` to obtain `DKColorPicker` from `DKColorTable` - - ```objectivec - view.dk_backgroundColorPicker = DKColorPickerWithKey(BG); - ``` - -+ Use `DKColorPickerWithRGB` to generate a `DKColorPicker` - - ```objectivec - view.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434); - ``` - -### DKColorTable - -`DKColorTable` is a new feature in DKNightVersion which providing us an elegant way to manage color setting in a project. Use as follows: - -There is a file called `DKColorTable.txt` - -``` -NORMAL NIGHT -# ffffff #343434 BG -# aaaaaa #313131 SEP -``` - -The first line of this file indicated different themes. **NORMAL is required column**, and others are optional. So if you don't need to integrate different themes in your app, just leave the first column in this file, like this: - -``` -NORMAL -# ffffff BG -# aaaaaa SEP -``` - -`NORMAL` and `NIGHT` are two different themes, `NORMAL` is default and for normal mode. `NIGHT` is optional and for night mode. - -You can add multiple columns in this `DKColorTable.txt` file as many as you want. - -``` -NORMAL NIGHT RED -# ffffff #343434 #ff0000 BG -# aaaaaa #313131 #ff0000 SEP -``` - -The last column is the key for a color entry, DKNightVersion use the current `themeVersion` (ex: `NORMAL` `NIGHT` and `RED`) and key (ex: `BG`, `SEP`) to find the corresponding color in DKColorTable. - -`DKColorTable` has a property `file`, it will loads the color setting in this `file` when `+ [DKColorTable sharedColorTable` is called. Default value of `file` is `DKColorTable.txt`. - -```objectivec -@property (nonatomic, strong) NSString *file; -``` - -You can also add another file into your project and fill your color setting in that file. - -``` -// color.txt -NORMAL NIGHT -# ffffff #343434 BG -``` - -And change `file` value - -```objectivec -[DKColorTable sharedColorTable].file = @"color.txt" -``` - -This will reload color setting from `color.txt` file. - -### Create temporary DKColorPicker - -If you'd want to create some temporary DKColorPicker, you can use these methods. - -```objectivec -view.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434); -``` - -`DKColorPickerWithRGB` will return a DKColorPicker which set background color to `#ffffff` when current theme version is `DKThemeVersionNormal` and `#343434` when it is `DKThemeVersionNight`. - -There are also some similar functions like `DKColorPickerWithColors` - -```objectivec -DKColorPicker DKColorPickerWithRGB(NSUInteger normal, ...); -DKColorPicker DKColorPickerWithColors(UIColor *normalColor, ...); -``` - -`DKColor` also provides a cluster of convenient `API` which returns `DKColorPicker` block, these blocks **return the same color in different themes**. - -```objectivec -+ (DKColorPicker)colorPickerWithUIColor:(UIColor *)color; - -+ (DKColorPicker)colorPickerWithWhite:(CGFloat)white alpha:(CGFloat)alpha; -+ (DKColorPicker)colorPickerWithHue:(CGFloat)hue saturation:(CGFloat)saturation brightness:(CGFloat)brightness alpha:(CGFloat)alpha; -+ (DKColorPicker)colorPickerWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; -+ (DKColorPicker)colorPickerWithCGColor:(CGColorRef)cgColor; -+ (DKColorPicker)colorPickerWithPatternImage:(UIImage *)image; -# if __has_include() -+ (DKColorPicker)colorPickerWithCIColor:(CIColor *)ciColor NS_AVAILABLE_IOS(5_0); -# endif - -+ (DKColorPicker)blackColor; -+ (DKColorPicker)darkGrayColor; -+ (DKColorPicker)lightGrayColor; -+ (DKColorPicker)whiteColor; -+ (DKColorPicker)grayColor; -+ (DKColorPicker)redColor; -+ (DKColorPicker)greenColor; -+ (DKColorPicker)blueColor; -+ (DKColorPicker)cyanColor; -+ (DKColorPicker)yellowColor; -+ (DKColorPicker)magentaColor; -+ (DKColorPicker)orangeColor; -+ (DKColorPicker)purpleColor; -+ (DKColorPicker)brownColor; -+ (DKColorPicker)clearColor; -``` - -### pickerify - -DKNightVersion provides a extremely powerful feature which can generates dk_xxxColorPicker with a macro called `pickerify`. - -```objectivec -@pickerify(TableViewCell, cellTintColor) -``` - -This will automatically generate `dk_cellTintColorPicker` for you. - - -### DKImagePicker - -Use `DKImagePicker` to change images when `manager.themeVersion` changes. - -```objectivec -imageView.dk_imagePicker = DKImagePickerWithNames(@"normal", @"night"); -``` - -The first image is for `NORMAL` the second is for `NIGHT`, cuz themes order in -DKColorTable.txt file is NORMAL NIGHT. - -If your file like this: - -``` -NORMAL NIGHT RED -# ffffff #343434 #fafafa BG -# aaaaaa #313131 #aaaaaa SEP -# 0000ff #ffffff #fa0000 TINT -# 000000 #ffffff #000000 TEXT -# ffffff #444444 #ffffff BAR -``` - -Set your image picker in this order: - -```objectivec -imageView.dk_imagePicker = DKImagePickerWithNames(@"normal", @"night", @"red"); -``` - -The order of images or names is exactly the same in DKColorTable.txt file. - -```objectivec -DKImagePicker DKImagePickerWithImages(UIImage *normalImage, ...); -DKImagePicker DKImagePickerWithNames(NSString *normalName, ...); -``` - -# Contribute - -Feel free to open an issue or pull request, if you need help or there is a bug. - -# Contact - -- Powered by [Draveness](http://github.com/draveness) -- Personal website [Draveness](http://draveness.me) - -# Todo - -- Documentation - -# License - -DKNightVersion is available under the MIT license. See the LICENSE file for more info. - -The MIT License (MIT) - -Copyright (c) 2015 Draveness - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/Pods/FMDB/LICENSE.txt b/Pods/FMDB/LICENSE.txt deleted file mode 100644 index addfc1a..0000000 --- a/Pods/FMDB/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -If you are using FMDB in your project, I'd love to hear about it. Let Gus know -by sending an email to gus@flyingmeat.com. - -And if you happen to come across either Gus Mueller or Rob Ryan in a bar, you -might consider purchasing a drink of their choosing if FMDB has been useful to -you. - -Finally, and shortly, this is the MIT License. - -Copyright (c) 2008-2014 Flying Meat Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/Pods/FMDB/README.markdown b/Pods/FMDB/README.markdown deleted file mode 100644 index f0d883d..0000000 --- a/Pods/FMDB/README.markdown +++ /dev/null @@ -1,397 +0,0 @@ -# FMDB v2.6.2 - -This is an Objective-C wrapper around SQLite: http://sqlite.org/ - -## The FMDB Mailing List: -http://groups.google.com/group/fmdb - -## Read the SQLite FAQ: -http://www.sqlite.org/faq.html - -Since FMDB is built on top of SQLite, you're going to want to read this page top to bottom at least once. And while you're there, make sure to bookmark the SQLite Documentation page: http://www.sqlite.org/docs.html - -## Contributing -Do you have an awesome idea that deserves to be in FMDB? You might consider pinging ccgus first to make sure he hasn't already ruled it out for some reason. Otherwise pull requests are great, and make sure you stick to the local coding conventions. However, please be patient and if you haven't heard anything from ccgus for a week or more, you might want to send a note asking what's up. - -## CocoaPods - -[![Dependency Status](https://www.versioneye.com/objective-c/fmdb/2.3/badge.svg?style=flat)](https://www.versioneye.com/objective-c/fmdb/2.3) -[![Reference Status](https://www.versioneye.com/objective-c/fmdb/reference_badge.svg?style=flat)](https://www.versioneye.com/objective-c/fmdb/references) - -FMDB can be installed using [CocoaPods](https://cocoapods.org/). - -``` -pod 'FMDB' -# pod 'FMDB/FTS' # FMDB with FTS -# pod 'FMDB/standalone' # FMDB with latest SQLite amalgamation source -# pod 'FMDB/standalone/FTS' # FMDB with latest SQLite amalgamation source and FTS -# pod 'FMDB/SQLCipher' # FMDB with SQLCipher -``` - -**If using FMDB with [SQLCipher](https://www.zetetic.net/sqlcipher/) you must use the FMDB/SQLCipher subspec. The FMDB/SQLCipher subspec declares SQLCipher as a dependency, allowing FMDB to be compiled with the `-DSQLITE_HAS_CODEC` flag.** - -## FMDB Class Reference: -http://ccgus.github.io/fmdb/html/index.html - -## Automatic Reference Counting (ARC) or Manual Memory Management? -You can use either style in your Cocoa project. FMDB will figure out which you are using at compile time and do the right thing. - -## Usage -There are three main classes in FMDB: - -1. `FMDatabase` - Represents a single SQLite database. Used for executing SQL statements. -2. `FMResultSet` - Represents the results of executing a query on an `FMDatabase`. -3. `FMDatabaseQueue` - If you're wanting to perform queries and updates on multiple threads, you'll want to use this class. It's described in the "Thread Safety" section below. - -### Database Creation -An `FMDatabase` is created with a path to a SQLite database file. This path can be one of these three: - -1. A file system path. The file does not have to exist on disk. If it does not exist, it is created for you. -2. An empty string (`@""`). An empty database is created at a temporary location. This database is deleted with the `FMDatabase` connection is closed. -3. `NULL`. An in-memory database is created. This database will be destroyed with the `FMDatabase` connection is closed. - -(For more information on temporary and in-memory databases, read the sqlite documentation on the subject: http://www.sqlite.org/inmemorydb.html) - -```objc -FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"]; -``` - -### Opening - -Before you can interact with the database, it must be opened. Opening fails if there are insufficient resources or permissions to open and/or create the database. - -```objc -if (![db open]) { - [db release]; - return; -} -``` - -### Executing Updates - -Any sort of SQL statement which is not a `SELECT` statement qualifies as an update. This includes `CREATE`, `UPDATE`, `INSERT`, `ALTER`, `COMMIT`, `BEGIN`, `DETACH`, `DELETE`, `DROP`, `END`, `EXPLAIN`, `VACUUM`, and `REPLACE` statements (plus many more). Basically, if your SQL statement does not begin with `SELECT`, it is an update statement. - -Executing updates returns a single value, a `BOOL`. A return value of `YES` means the update was successfully executed, and a return value of `NO` means that some error was encountered. You may invoke the `-lastErrorMessage` and `-lastErrorCode` methods to retrieve more information. - -### Executing Queries - -A `SELECT` statement is a query and is executed via one of the `-executeQuery...` methods. - -Executing queries returns an `FMResultSet` object if successful, and `nil` upon failure. You should use the `-lastErrorMessage` and `-lastErrorCode` methods to determine why a query failed. - -In order to iterate through the results of your query, you use a `while()` loop. You also need to "step" from one record to the other. With FMDB, the easiest way to do that is like this: - -```objc -FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"]; -while ([s next]) { - //retrieve values for each record -} -``` - -You must always invoke `-[FMResultSet next]` before attempting to access the values returned in a query, even if you're only expecting one: - -```objc -FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"]; -if ([s next]) { - int totalCount = [s intForColumnIndex:0]; -} -``` - -`FMResultSet` has many methods to retrieve data in an appropriate format: - -- `intForColumn:` -- `longForColumn:` -- `longLongIntForColumn:` -- `boolForColumn:` -- `doubleForColumn:` -- `stringForColumn:` -- `dateForColumn:` -- `dataForColumn:` -- `dataNoCopyForColumn:` -- `UTF8StringForColumnName:` -- `objectForColumnName:` - -Each of these methods also has a `{type}ForColumnIndex:` variant that is used to retrieve the data based on the position of the column in the results, as opposed to the column's name. - -Typically, there's no need to `-close` an `FMResultSet` yourself, since that happens when either the result set is deallocated, or the parent database is closed. - -### Closing - -When you have finished executing queries and updates on the database, you should `-close` the `FMDatabase` connection so that SQLite will relinquish any resources it has acquired during the course of its operation. - -```objc -[db close]; -``` - -### Transactions - -`FMDatabase` can begin and commit a transaction by invoking one of the appropriate methods or executing a begin/end transaction statement. - -### Multiple Statements and Batch Stuff - -You can use `FMDatabase`'s executeStatements:withResultBlock: to do multiple statements in a string: - -```objc -NSString *sql = @"create table bulktest1 (id integer primary key autoincrement, x text);" - "create table bulktest2 (id integer primary key autoincrement, y text);" - "create table bulktest3 (id integer primary key autoincrement, z text);" - "insert into bulktest1 (x) values ('XXX');" - "insert into bulktest2 (y) values ('YYY');" - "insert into bulktest3 (z) values ('ZZZ');"; - -success = [db executeStatements:sql]; - -sql = @"select count(*) as count from bulktest1;" - "select count(*) as count from bulktest2;" - "select count(*) as count from bulktest3;"; - -success = [self.db executeStatements:sql withResultBlock:^int(NSDictionary *dictionary) { - NSInteger count = [dictionary[@"count"] integerValue]; - XCTAssertEqual(count, 1, @"expected one record for dictionary %@", dictionary); - return 0; -}]; -``` - -### Data Sanitization - -When providing a SQL statement to FMDB, you should not attempt to "sanitize" any values before insertion. Instead, you should use the standard SQLite binding syntax: - -```sql -INSERT INTO myTable VALUES (?, ?, ?, ?) -``` - -The `?` character is recognized by SQLite as a placeholder for a value to be inserted. The execution methods all accept a variable number of arguments (or a representation of those arguments, such as an `NSArray`, `NSDictionary`, or a `va_list`), which are properly escaped for you. - -And, to use that SQL with the `?` placeholders from Objective-C: - -```objc -NSInteger identifier = 42; -NSString *name = @"Liam O'Flaherty (\"the famous Irish author\")"; -NSDate *date = [NSDate date]; -NSString *comment = nil; - -BOOL success = [db executeUpdate:@"INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)", @(identifier), name, date, comment ?: [NSNull null]]; -if (!success) { - NSLog(@"error = %@", [db lastErrorMessage]); -} -``` - -> **Note:** Fundamental data types, like the `NSInteger` variable `identifier`, should be as a `NSNumber` objects, achieved by using the `@` syntax, shown above. Or you can use the `[NSNumber numberWithInt:identifier]` syntax, too. -> -> Likewise, SQL `NULL` values should be inserted as `[NSNull null]`. For example, in the case of `comment` which might be `nil` (and is in this example), you can use the `comment ?: [NSNull null]` syntax, which will insert the string if `comment` is not `nil`, but will insert `[NSNull null]` if it is `nil`. - -In Swift, you would use `executeUpdate(values:)`, which not only is a concise Swift syntax, but also `throws` errors for proper Swift 2 error handling: - -```swift -do { - let identifier = 42 - let name = "Liam O'Flaherty (\"the famous Irish author\")" - let date = NSDate() - let comment: String? = nil - - try db.executeUpdate("INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)", values: [identifier, name, date, comment ?? NSNull()]) -} catch { - print("error = \(error)") -} -``` - -> **Note:** In Swift, you don't have to wrap fundamental numeric types like you do in Objective-C. But if you are going to insert an optional string, you would probably use the `comment ?? NSNull()` syntax (i.e., if it is `nil`, use `NSNull`, otherwise use the string). - -Alternatively, you may use named parameters syntax: - -```sql -INSERT INTO authors (identifier, name, date, comment) VALUES (:identifier, :name, :date, :comment) -``` - -The parameters *must* start with a colon. SQLite itself supports other characters, but internally the dictionary keys are prefixed with a colon, do **not** include the colon in your dictionary keys. - -```objc -NSDictionary *arguments = @{@"identifier": @(identifier), @"name": name, @"date": date, @"comment": comment ?: [NSNull null]}; -BOOL success = [db executeUpdate:@"INSERT INTO authors (identifier, name, date, comment) VALUES (:identifier, :name, :date, :comment)" withParameterDictionary:arguments]; -if (!success) { - NSLog(@"error = %@", [db lastErrorMessage]); -} -``` - -The key point is that one should not use `NSString` method `stringWithFormat` to manually insert values into the SQL statement, itself. Nor should one Swift string interpolation to insert values into the SQL. Use `?` placeholders for values to be inserted into the database (or used in `WHERE` clauses in `SELECT` statements). - -

Using FMDatabaseQueue and Thread Safety.

- -Using a single instance of `FMDatabase` from multiple threads at once is a bad idea. It has always been OK to make a `FMDatabase` object *per thread*. Just don't share a single instance across threads, and definitely not across multiple threads at the same time. Bad things will eventually happen and you'll eventually get something to crash, or maybe get an exception, or maybe meteorites will fall out of the sky and hit your Mac Pro. *This would suck*. - -**So don't instantiate a single `FMDatabase` object and use it across multiple threads.** - -Instead, use `FMDatabaseQueue`. Instantiate a single `FMDatabaseQueue` and use it across multiple threads. The `FMDatabaseQueue` object will synchronize and coordinate access across the multiple threads. Here's how to use it: - -First, make your queue. - -```objc -FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; -``` - -Then use it like so: - - -```objc -[queue inDatabase:^(FMDatabase *db) { - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @1]; - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2]; - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @3]; - - FMResultSet *rs = [db executeQuery:@"select * from foo"]; - while ([rs next]) { - … - } -}]; -``` - -An easy way to wrap things up in a transaction can be done like this: - -```objc -[queue inTransaction:^(FMDatabase *db, BOOL *rollback) { - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @1]; - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2]; - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @3]; - - if (whoopsSomethingWrongHappened) { - *rollback = YES; - return; - } - // etc… - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @4]; -}]; -``` - -The Swift equivalent would be: - -```swift -queue.inTransaction { db, rollback in - do { - try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1]) - try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [2]) - try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [3]) - - if whoopsSomethingWrongHappened { - rollback.memory = true - return - } - - try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [4]) - } catch { - rollback.memory = true - print(error) - } -} -``` - -`FMDatabaseQueue` will run the blocks on a serialized queue (hence the name of the class). So if you call `FMDatabaseQueue`'s methods from multiple threads at the same time, they will be executed in the order they are received. This way queries and updates won't step on each other's toes, and every one is happy. - -**Note:** The calls to `FMDatabaseQueue`'s methods are blocking. So even though you are passing along blocks, they will **not** be run on another thread. - -## Making custom sqlite functions, based on blocks. - -You can do this! For an example, look for `-makeFunctionNamed:` in main.m - -## Swift - -You can use FMDB in Swift projects too. - -To do this, you must: - -1. Copy the relevant `.m` and `.h` files from the FMDB `src` folder into your project. - - You can copy all of them (which is easiest), or only the ones you need. Likely you will need [`FMDatabase`](http://ccgus.github.io/fmdb/html/Classes/FMDatabase.html) and [`FMResultSet`](http://ccgus.github.io/fmdb/html/Classes/FMResultSet.html) at a minimum. [`FMDatabaseAdditions`](http://ccgus.github.io/fmdb/html/Categories/FMDatabase+FMDatabaseAdditions.html) provides some very useful convenience methods, so you will likely want that, too. If you are doing multithreaded access to a database, [`FMDatabaseQueue`](http://ccgus.github.io/fmdb/html/Classes/FMDatabaseQueue.html) is quite useful, too. If you choose to not copy all of the files from the `src` directory, though, you may want to update `FMDB.h` to only reference the files that you included in your project. - - Note, if you're copying all of the files from the `src` folder into to your project (which is recommended), you may want to drag the individual files into your project, not the folder, itself, because if you drag the folder, you won't be prompted to add the bridging header (see next point). - -2. If prompted to create a "bridging header", you should do so. If not prompted and if you don't already have a bridging header, add one. - - For more information on bridging headers, see [Swift and Objective-C in the Same Project](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_76). - -3. In your bridging header, add a line that says: - ```objc - #import "FMDB.h" - ``` - -4. Use the variations of `executeQuery` and `executeUpdate` with the `sql` and `values` parameters with `try` pattern, as shown below. These renditions of `executeQuery` and `executeUpdate` both `throw` errors in true Swift 2 fashion. - -If you do the above, you can then write Swift code that uses `FMDatabase`. For example: - -```swift -let documents = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false) -let fileURL = documents.URLByAppendingPathComponent("test.sqlite") - -let database = FMDatabase(path: fileURL.path) - -if !database.open() { - print("Unable to open database") - return -} - -do { - try database.executeUpdate("create table test(x text, y text, z text)", values: nil) - try database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", values: ["a", "b", "c"]) - try database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", values: ["e", "f", "g"]) - - let rs = try database.executeQuery("select x, y, z from test", values: nil) - while rs.next() { - let x = rs.stringForColumn("x") - let y = rs.stringForColumn("y") - let z = rs.stringForColumn("z") - print("x = \(x); y = \(y); z = \(z)") - } -} catch let error as NSError { - print("failed: \(error.localizedDescription)") -} - -database.close() -``` - -## History - -The history and changes are availbe on its [GitHub page](https://github.com/ccgus/fmdb) and are summarized in the "CHANGES_AND_TODO_LIST.txt" file. - -## Contributors - -The contributors to FMDB are contained in the "Contributors.txt" file. - -## Additional projects using FMDB, which might be interesting to the discerning developer. - - * FMDBMigrationManager, A SQLite schema migration management system for FMDB: https://github.com/layerhq/FMDBMigrationManager - * FCModel, An alternative to Core Data for people who like having direct SQL access: https://github.com/marcoarment/FCModel - -## Quick notes on FMDB's coding style - -Spaces, not tabs. Square brackets, not dot notation. Look at what FMDB already does with curly brackets and such, and stick to that style. - -## Reporting bugs - -Reduce your bug down to the smallest amount of code possible. You want to make it super easy for the developers to see and reproduce your bug. If it helps, pretend that the person who can fix your bug is active on shipping 3 major products, works on a handful of open source projects, has a newborn baby, and is generally very very busy. - -And we've even added a template function to main.m (FMDBReportABugFunction) in the FMDB distribution to help you out: - -* Open up fmdb project in Xcode. -* Open up main.m and modify the FMDBReportABugFunction to reproduce your bug. - * Setup your table(s) in the code. - * Make your query or update(s). - * Add some assertions which demonstrate the bug. - -Then you can bring it up on the FMDB mailing list by showing your nice and compact FMDBReportABugFunction, or you can report the bug via the github FMDB bug reporter. - -**Optional:** - -Figure out where the bug is, fix it, and send a patch in or bring that up on the mailing list. Make sure all the other tests run after your modifications. - -## Support - -The support channels for FMDB are the mailing list (see above), filing a bug here, or maybe on Stack Overflow. So that is to say, support is provided by the community and on a voluntary basis. - -FMDB development is overseen by Gus Mueller of Flying Meat. If FMDB been helpful to you, consider purchasing an app from FM or telling all your friends about it. - -## License - -The license for FMDB is contained in the "License.txt" file. - -If you happen to come across either Gus Mueller or Rob Ryan in a bar, you might consider purchasing a drink of their choosing if FMDB has been useful to you. - -(The drink is for them of course, shame on you for trying to keep it.) diff --git a/Pods/FMDB/src/fmdb/FMDB.h b/Pods/FMDB/src/fmdb/FMDB.h deleted file mode 100644 index 1ff5465..0000000 --- a/Pods/FMDB/src/fmdb/FMDB.h +++ /dev/null @@ -1,10 +0,0 @@ -#import - -FOUNDATION_EXPORT double FMDBVersionNumber; -FOUNDATION_EXPORT const unsigned char FMDBVersionString[]; - -#import "FMDatabase.h" -#import "FMResultSet.h" -#import "FMDatabaseAdditions.h" -#import "FMDatabaseQueue.h" -#import "FMDatabasePool.h" diff --git a/Pods/FMDB/src/fmdb/FMDatabase.h b/Pods/FMDB/src/fmdb/FMDatabase.h deleted file mode 100644 index 7dd5f8c..0000000 --- a/Pods/FMDB/src/fmdb/FMDatabase.h +++ /dev/null @@ -1,1162 +0,0 @@ -#import -#import "FMResultSet.h" -#import "FMDatabasePool.h" - - -#if ! __has_feature(objc_arc) - #define FMDBAutorelease(__v) ([__v autorelease]); - #define FMDBReturnAutoreleased FMDBAutorelease - - #define FMDBRetain(__v) ([__v retain]); - #define FMDBReturnRetained FMDBRetain - - #define FMDBRelease(__v) ([__v release]); - - #define FMDBDispatchQueueRelease(__v) (dispatch_release(__v)); -#else - // -fobjc-arc - #define FMDBAutorelease(__v) - #define FMDBReturnAutoreleased(__v) (__v) - - #define FMDBRetain(__v) - #define FMDBReturnRetained(__v) (__v) - - #define FMDBRelease(__v) - -// If OS_OBJECT_USE_OBJC=1, then the dispatch objects will be treated like ObjC objects -// and will participate in ARC. -// See the section on "Dispatch Queues and Automatic Reference Counting" in "Grand Central Dispatch (GCD) Reference" for details. - #if OS_OBJECT_USE_OBJC - #define FMDBDispatchQueueRelease(__v) - #else - #define FMDBDispatchQueueRelease(__v) (dispatch_release(__v)); - #endif -#endif - -#if !__has_feature(objc_instancetype) - #define instancetype id -#endif - - -typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary); - - -/** A SQLite ([http://sqlite.org/](http://sqlite.org/)) Objective-C wrapper. - - ### Usage - The three main classes in FMDB are: - - - `FMDatabase` - Represents a single SQLite database. Used for executing SQL statements. - - `` - Represents the results of executing a query on an `FMDatabase`. - - `` - If you want to perform queries and updates on multiple threads, you'll want to use this class. - - ### See also - - - `` - A pool of `FMDatabase` objects. - - `` - A wrapper for `sqlite_stmt`. - - ### External links - - - [FMDB on GitHub](https://github.com/ccgus/fmdb) including introductory documentation - - [SQLite web site](http://sqlite.org/) - - [FMDB mailing list](http://groups.google.com/group/fmdb) - - [SQLite FAQ](http://www.sqlite.org/faq.html) - - @warning Do not instantiate a single `FMDatabase` object and use it across multiple threads. Instead, use ``. - - */ - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-interface-ivars" - - -@interface FMDatabase : NSObject { - - void* _db; - NSString* _databasePath; - BOOL _logsErrors; - BOOL _crashOnErrors; - BOOL _traceExecution; - BOOL _checkedOut; - BOOL _shouldCacheStatements; - BOOL _isExecutingStatement; - BOOL _inTransaction; - NSTimeInterval _maxBusyRetryTimeInterval; - NSTimeInterval _startBusyRetryTime; - - NSMutableDictionary *_cachedStatements; - NSMutableSet *_openResultSets; - NSMutableSet *_openFunctions; - - NSDateFormatter *_dateFormat; -} - -///----------------- -/// @name Properties -///----------------- - -/** Whether should trace execution */ - -@property (atomic, assign) BOOL traceExecution; - -/** Whether checked out or not */ - -@property (atomic, assign) BOOL checkedOut; - -/** Crash on errors */ - -@property (atomic, assign) BOOL crashOnErrors; - -/** Logs errors */ - -@property (atomic, assign) BOOL logsErrors; - -/** Dictionary of cached statements */ - -@property (atomic, retain) NSMutableDictionary *cachedStatements; - -///--------------------- -/// @name Initialization -///--------------------- - -/** Create a `FMDatabase` object. - - An `FMDatabase` is created with a path to a SQLite database file. This path can be one of these three: - - 1. A file system path. The file does not have to exist on disk. If it does not exist, it is created for you. - 2. An empty string (`@""`). An empty database is created at a temporary location. This database is deleted with the `FMDatabase` connection is closed. - 3. `nil`. An in-memory database is created. This database will be destroyed with the `FMDatabase` connection is closed. - - For example, to create/open a database in your Mac OS X `tmp` folder: - - FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"]; - - Or, in iOS, you might open a database in the app's `Documents` directory: - - NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; - NSString *dbPath = [docsPath stringByAppendingPathComponent:@"test.db"]; - FMDatabase *db = [FMDatabase databaseWithPath:dbPath]; - - (For more information on temporary and in-memory databases, read the sqlite documentation on the subject: [http://www.sqlite.org/inmemorydb.html](http://www.sqlite.org/inmemorydb.html)) - - @param inPath Path of database file - - @return `FMDatabase` object if successful; `nil` if failure. - - */ - -+ (instancetype)databaseWithPath:(NSString*)inPath; - -/** Initialize a `FMDatabase` object. - - An `FMDatabase` is created with a path to a SQLite database file. This path can be one of these three: - - 1. A file system path. The file does not have to exist on disk. If it does not exist, it is created for you. - 2. An empty string (`@""`). An empty database is created at a temporary location. This database is deleted with the `FMDatabase` connection is closed. - 3. `nil`. An in-memory database is created. This database will be destroyed with the `FMDatabase` connection is closed. - - For example, to create/open a database in your Mac OS X `tmp` folder: - - FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"]; - - Or, in iOS, you might open a database in the app's `Documents` directory: - - NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; - NSString *dbPath = [docsPath stringByAppendingPathComponent:@"test.db"]; - FMDatabase *db = [FMDatabase databaseWithPath:dbPath]; - - (For more information on temporary and in-memory databases, read the sqlite documentation on the subject: [http://www.sqlite.org/inmemorydb.html](http://www.sqlite.org/inmemorydb.html)) - - @param inPath Path of database file - - @return `FMDatabase` object if successful; `nil` if failure. - - */ - -- (instancetype)initWithPath:(NSString*)inPath; - - -///----------------------------------- -/// @name Opening and closing database -///----------------------------------- - -/** Opening a new database connection - - The database is opened for reading and writing, and is created if it does not already exist. - - @return `YES` if successful, `NO` on error. - - @see [sqlite3_open()](http://sqlite.org/c3ref/open.html) - @see openWithFlags: - @see close - */ - -- (BOOL)open; - -/** Opening a new database connection with flags and an optional virtual file system (VFS) - - @param flags one of the following three values, optionally combined with the `SQLITE_OPEN_NOMUTEX`, `SQLITE_OPEN_FULLMUTEX`, `SQLITE_OPEN_SHAREDCACHE`, `SQLITE_OPEN_PRIVATECACHE`, and/or `SQLITE_OPEN_URI` flags: - - `SQLITE_OPEN_READONLY` - - The database is opened in read-only mode. If the database does not already exist, an error is returned. - - `SQLITE_OPEN_READWRITE` - - The database is opened for reading and writing if possible, or reading only if the file is write protected by the operating system. In either case the database must already exist, otherwise an error is returned. - - `SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE` - - The database is opened for reading and writing, and is created if it does not already exist. This is the behavior that is always used for `open` method. - - @return `YES` if successful, `NO` on error. - - @see [sqlite3_open_v2()](http://sqlite.org/c3ref/open.html) - @see open - @see close - */ - -- (BOOL)openWithFlags:(int)flags; - -/** Opening a new database connection with flags and an optional virtual file system (VFS) - - @param flags one of the following three values, optionally combined with the `SQLITE_OPEN_NOMUTEX`, `SQLITE_OPEN_FULLMUTEX`, `SQLITE_OPEN_SHAREDCACHE`, `SQLITE_OPEN_PRIVATECACHE`, and/or `SQLITE_OPEN_URI` flags: - - `SQLITE_OPEN_READONLY` - - The database is opened in read-only mode. If the database does not already exist, an error is returned. - - `SQLITE_OPEN_READWRITE` - - The database is opened for reading and writing if possible, or reading only if the file is write protected by the operating system. In either case the database must already exist, otherwise an error is returned. - - `SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE` - - The database is opened for reading and writing, and is created if it does not already exist. This is the behavior that is always used for `open` method. - - @param vfsName If vfs is given the value is passed to the vfs parameter of sqlite3_open_v2. - - @return `YES` if successful, `NO` on error. - - @see [sqlite3_open_v2()](http://sqlite.org/c3ref/open.html) - @see open - @see close - */ - -- (BOOL)openWithFlags:(int)flags vfs:(NSString *)vfsName; - -/** Closing a database connection - - @return `YES` if success, `NO` on error. - - @see [sqlite3_close()](http://sqlite.org/c3ref/close.html) - @see open - @see openWithFlags: - */ - -- (BOOL)close; - -/** Test to see if we have a good connection to the database. - - This will confirm whether: - - - is database open - - if open, it will try a simple SELECT statement and confirm that it succeeds. - - @return `YES` if everything succeeds, `NO` on failure. - */ - -- (BOOL)goodConnection; - - -///---------------------- -/// @name Perform updates -///---------------------- - -/** Execute single update statement - - This method executes a single SQL update statement (i.e. any SQL that does not return results, such as `UPDATE`, `INSERT`, or `DELETE`. This method employs [`sqlite3_prepare_v2`](http://sqlite.org/c3ref/prepare.html), [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) to bind values to `?` placeholders in the SQL with the optional list of parameters, and [`sqlite_step`](http://sqlite.org/c3ref/step.html) to perform the update. - - The optional values provided to this method should be objects (e.g. `NSString`, `NSNumber`, `NSNull`, `NSDate`, and `NSData` objects), not fundamental data types (e.g. `int`, `long`, `NSInteger`, etc.). This method automatically handles the aforementioned object types, and all other object types will be interpreted as text values using the object's `description` method. - - @param sql The SQL to be performed, with optional `?` placeholders. - - @param outErr A reference to the `NSError` pointer to be updated with an auto released `NSError` object if an error if an error occurs. If `nil`, no `NSError` object will be returned. - - @param ... Optional parameters to bind to `?` placeholders in the SQL statement. These should be Objective-C objects (e.g. `NSString`, `NSNumber`, etc.), not fundamental C data types (e.g. `int`, `char *`, etc.). - - @return `YES` upon success; `NO` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see lastError - @see lastErrorCode - @see lastErrorMessage - @see [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) - */ - -- (BOOL)executeUpdate:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ...; - -/** Execute single update statement - - @see executeUpdate:withErrorAndBindings: - - @warning **Deprecated**: Please use `` instead. - */ - -- (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... __attribute__ ((deprecated)); - -/** Execute single update statement - - This method executes a single SQL update statement (i.e. any SQL that does not return results, such as `UPDATE`, `INSERT`, or `DELETE`. This method employs [`sqlite3_prepare_v2`](http://sqlite.org/c3ref/prepare.html), [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) to bind values to `?` placeholders in the SQL with the optional list of parameters, and [`sqlite_step`](http://sqlite.org/c3ref/step.html) to perform the update. - - The optional values provided to this method should be objects (e.g. `NSString`, `NSNumber`, `NSNull`, `NSDate`, and `NSData` objects), not fundamental data types (e.g. `int`, `long`, `NSInteger`, etc.). This method automatically handles the aforementioned object types, and all other object types will be interpreted as text values using the object's `description` method. - - @param sql The SQL to be performed, with optional `?` placeholders. - - @param ... Optional parameters to bind to `?` placeholders in the SQL statement. These should be Objective-C objects (e.g. `NSString`, `NSNumber`, etc.), not fundamental C data types (e.g. `int`, `char *`, etc.). - - @return `YES` upon success; `NO` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see lastError - @see lastErrorCode - @see lastErrorMessage - @see [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) - - @note This technique supports the use of `?` placeholders in the SQL, automatically binding any supplied value parameters to those placeholders. This approach is more robust than techniques that entail using `stringWithFormat` to manually build SQL statements, which can be problematic if the values happened to include any characters that needed to be quoted. - - @note If you want to use this from Swift, please note that you must include `FMDatabaseVariadic.swift` in your project. Without that, you cannot use this method directly, and instead have to use methods such as ``. - */ - -- (BOOL)executeUpdate:(NSString*)sql, ...; - -/** Execute single update statement - - This method executes a single SQL update statement (i.e. any SQL that does not return results, such as `UPDATE`, `INSERT`, or `DELETE`. This method employs [`sqlite3_prepare_v2`](http://sqlite.org/c3ref/prepare.html) and [`sqlite_step`](http://sqlite.org/c3ref/step.html) to perform the update. Unlike the other `executeUpdate` methods, this uses printf-style formatters (e.g. `%s`, `%d`, etc.) to build the SQL. Do not use `?` placeholders in the SQL if you use this method. - - @param format The SQL to be performed, with `printf`-style escape sequences. - - @param ... Optional parameters to bind to use in conjunction with the `printf`-style escape sequences in the SQL statement. - - @return `YES` upon success; `NO` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see executeUpdate: - @see lastError - @see lastErrorCode - @see lastErrorMessage - - @note This method does not technically perform a traditional printf-style replacement. What this method actually does is replace the printf-style percent sequences with a SQLite `?` placeholder, and then bind values to that placeholder. Thus the following command - - [db executeUpdateWithFormat:@"INSERT INTO test (name) VALUES (%@)", @"Gus"]; - - is actually replacing the `%@` with `?` placeholder, and then performing something equivalent to `` - - [db executeUpdate:@"INSERT INTO test (name) VALUES (?)", @"Gus"]; - - There are two reasons why this distinction is important. First, the printf-style escape sequences can only be used where it is permissible to use a SQLite `?` placeholder. You can use it only for values in SQL statements, but not for table names or column names or any other non-value context. This method also cannot be used in conjunction with `pragma` statements and the like. Second, note the lack of quotation marks in the SQL. The `VALUES` clause was _not_ `VALUES ('%@')` (like you might have to do if you built a SQL statement using `NSString` method `stringWithFormat`), but rather simply `VALUES (%@)`. - */ - -- (BOOL)executeUpdateWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2); - -/** Execute single update statement - - This method executes a single SQL update statement (i.e. any SQL that does not return results, such as `UPDATE`, `INSERT`, or `DELETE`. This method employs [`sqlite3_prepare_v2`](http://sqlite.org/c3ref/prepare.html) and [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) binding any `?` placeholders in the SQL with the optional list of parameters. - - The optional values provided to this method should be objects (e.g. `NSString`, `NSNumber`, `NSNull`, `NSDate`, and `NSData` objects), not fundamental data types (e.g. `int`, `long`, `NSInteger`, etc.). This method automatically handles the aforementioned object types, and all other object types will be interpreted as text values using the object's `description` method. - - @param sql The SQL to be performed, with optional `?` placeholders. - - @param arguments A `NSArray` of objects to be used when binding values to the `?` placeholders in the SQL statement. - - @return `YES` upon success; `NO` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see executeUpdate:values:error: - @see lastError - @see lastErrorCode - @see lastErrorMessage - */ - -- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments; - -/** Execute single update statement - - This method executes a single SQL update statement (i.e. any SQL that does not return results, such as `UPDATE`, `INSERT`, or `DELETE`. This method employs [`sqlite3_prepare_v2`](http://sqlite.org/c3ref/prepare.html) and [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) binding any `?` placeholders in the SQL with the optional list of parameters. - - The optional values provided to this method should be objects (e.g. `NSString`, `NSNumber`, `NSNull`, `NSDate`, and `NSData` objects), not fundamental data types (e.g. `int`, `long`, `NSInteger`, etc.). This method automatically handles the aforementioned object types, and all other object types will be interpreted as text values using the object's `description` method. - - This is similar to ``, except that this also accepts a pointer to a `NSError` pointer, so that errors can be returned. - - In Swift 2, this throws errors, as if it were defined as follows: - - `func executeUpdate(sql: String!, values: [AnyObject]!) throws -> Bool` - - @param sql The SQL to be performed, with optional `?` placeholders. - - @param values A `NSArray` of objects to be used when binding values to the `?` placeholders in the SQL statement. - - @param error A `NSError` object to receive any error object (if any). - - @return `YES` upon success; `NO` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see lastError - @see lastErrorCode - @see lastErrorMessage - - */ - -- (BOOL)executeUpdate:(NSString*)sql values:(NSArray *)values error:(NSError * __autoreleasing *)error; - -/** Execute single update statement - - This method executes a single SQL update statement (i.e. any SQL that does not return results, such as `UPDATE`, `INSERT`, or `DELETE`. This method employs [`sqlite3_prepare_v2`](http://sqlite.org/c3ref/prepare.html) and [`sqlite_step`](http://sqlite.org/c3ref/step.html) to perform the update. Unlike the other `executeUpdate` methods, this uses printf-style formatters (e.g. `%s`, `%d`, etc.) to build the SQL. - - The optional values provided to this method should be objects (e.g. `NSString`, `NSNumber`, `NSNull`, `NSDate`, and `NSData` objects), not fundamental data types (e.g. `int`, `long`, `NSInteger`, etc.). This method automatically handles the aforementioned object types, and all other object types will be interpreted as text values using the object's `description` method. - - @param sql The SQL to be performed, with optional `?` placeholders. - - @param arguments A `NSDictionary` of objects keyed by column names that will be used when binding values to the `?` placeholders in the SQL statement. - - @return `YES` upon success; `NO` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see lastError - @see lastErrorCode - @see lastErrorMessage -*/ - -- (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments; - - -/** Execute single update statement - - This method executes a single SQL update statement (i.e. any SQL that does not return results, such as `UPDATE`, `INSERT`, or `DELETE`. This method employs [`sqlite3_prepare_v2`](http://sqlite.org/c3ref/prepare.html) and [`sqlite_step`](http://sqlite.org/c3ref/step.html) to perform the update. Unlike the other `executeUpdate` methods, this uses printf-style formatters (e.g. `%s`, `%d`, etc.) to build the SQL. - - The optional values provided to this method should be objects (e.g. `NSString`, `NSNumber`, `NSNull`, `NSDate`, and `NSData` objects), not fundamental data types (e.g. `int`, `long`, `NSInteger`, etc.). This method automatically handles the aforementioned object types, and all other object types will be interpreted as text values using the object's `description` method. - - @param sql The SQL to be performed, with optional `?` placeholders. - - @param args A `va_list` of arguments. - - @return `YES` upon success; `NO` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see lastError - @see lastErrorCode - @see lastErrorMessage - */ - -- (BOOL)executeUpdate:(NSString*)sql withVAList: (va_list)args; - -/** Execute multiple SQL statements - - This executes a series of SQL statements that are combined in a single string (e.g. the SQL generated by the `sqlite3` command line `.dump` command). This accepts no value parameters, but rather simply expects a single string with multiple SQL statements, each terminated with a semicolon. This uses `sqlite3_exec`. - - @param sql The SQL to be performed - - @return `YES` upon success; `NO` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see executeStatements:withResultBlock: - @see [sqlite3_exec()](http://sqlite.org/c3ref/exec.html) - - */ - -- (BOOL)executeStatements:(NSString *)sql; - -/** Execute multiple SQL statements with callback handler - - This executes a series of SQL statements that are combined in a single string (e.g. the SQL generated by the `sqlite3` command line `.dump` command). This accepts no value parameters, but rather simply expects a single string with multiple SQL statements, each terminated with a semicolon. This uses `sqlite3_exec`. - - @param sql The SQL to be performed. - @param block A block that will be called for any result sets returned by any SQL statements. - Note, if you supply this block, it must return integer value, zero upon success (this would be a good opportunity to use SQLITE_OK), - non-zero value upon failure (which will stop the bulk execution of the SQL). If a statement returns values, the block will be called with the results from the query in NSDictionary *resultsDictionary. - This may be `nil` if you don't care to receive any results. - - @return `YES` upon success; `NO` upon failure. If failed, you can call ``, - ``, or `` for diagnostic information regarding the failure. - - @see executeStatements: - @see [sqlite3_exec()](http://sqlite.org/c3ref/exec.html) - - */ - -- (BOOL)executeStatements:(NSString *)sql withResultBlock:(FMDBExecuteStatementsCallbackBlock)block; - -/** Last insert rowid - - Each entry in an SQLite table has a unique 64-bit signed integer key called the "rowid". The rowid is always available as an undeclared column named `ROWID`, `OID`, or `_ROWID_` as long as those names are not also used by explicitly declared columns. If the table has a column of type `INTEGER PRIMARY KEY` then that column is another alias for the rowid. - - This routine returns the rowid of the most recent successful `INSERT` into the database from the database connection in the first argument. As of SQLite version 3.7.7, this routines records the last insert rowid of both ordinary tables and virtual tables. If no successful `INSERT`s have ever occurred on that database connection, zero is returned. - - @return The rowid of the last inserted row. - - @see [sqlite3_last_insert_rowid()](http://sqlite.org/c3ref/last_insert_rowid.html) - - */ - -- (int64_t)lastInsertRowId; - -/** The number of rows changed by prior SQL statement. - - This function returns the number of database rows that were changed or inserted or deleted by the most recently completed SQL statement on the database connection specified by the first parameter. Only changes that are directly specified by the INSERT, UPDATE, or DELETE statement are counted. - - @return The number of rows changed by prior SQL statement. - - @see [sqlite3_changes()](http://sqlite.org/c3ref/changes.html) - - */ - -- (int)changes; - - -///------------------------- -/// @name Retrieving results -///------------------------- - -/** Execute select statement - - Executing queries returns an `` object if successful, and `nil` upon failure. Like executing updates, there is a variant that accepts an `NSError **` parameter. Otherwise you should use the `` and `` methods to determine why a query failed. - - In order to iterate through the results of your query, you use a `while()` loop. You also need to "step" (via `<[FMResultSet next]>`) from one record to the other. - - This method employs [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) for any optional value parameters. This properly escapes any characters that need escape sequences (e.g. quotation marks), which eliminates simple SQL errors as well as protects against SQL injection attacks. This method natively handles `NSString`, `NSNumber`, `NSNull`, `NSDate`, and `NSData` objects. All other object types will be interpreted as text values using the object's `description` method. - - @param sql The SELECT statement to be performed, with optional `?` placeholders. - - @param ... Optional parameters to bind to `?` placeholders in the SQL statement. These should be Objective-C objects (e.g. `NSString`, `NSNumber`, etc.), not fundamental C data types (e.g. `int`, `char *`, etc.). - - @return A `` for the result set upon success; `nil` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see FMResultSet - @see [`FMResultSet next`](<[FMResultSet next]>) - @see [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) - - @note If you want to use this from Swift, please note that you must include `FMDatabaseVariadic.swift` in your project. Without that, you cannot use this method directly, and instead have to use methods such as ``. - */ - -- (FMResultSet *)executeQuery:(NSString*)sql, ...; - -/** Execute select statement - - Executing queries returns an `` object if successful, and `nil` upon failure. Like executing updates, there is a variant that accepts an `NSError **` parameter. Otherwise you should use the `` and `` methods to determine why a query failed. - - In order to iterate through the results of your query, you use a `while()` loop. You also need to "step" (via `<[FMResultSet next]>`) from one record to the other. - - @param format The SQL to be performed, with `printf`-style escape sequences. - - @param ... Optional parameters to bind to use in conjunction with the `printf`-style escape sequences in the SQL statement. - - @return A `` for the result set upon success; `nil` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see executeQuery: - @see FMResultSet - @see [`FMResultSet next`](<[FMResultSet next]>) - - @note This method does not technically perform a traditional printf-style replacement. What this method actually does is replace the printf-style percent sequences with a SQLite `?` placeholder, and then bind values to that placeholder. Thus the following command - - [db executeQueryWithFormat:@"SELECT * FROM test WHERE name=%@", @"Gus"]; - - is actually replacing the `%@` with `?` placeholder, and then performing something equivalent to `` - - [db executeQuery:@"SELECT * FROM test WHERE name=?", @"Gus"]; - - There are two reasons why this distinction is important. First, the printf-style escape sequences can only be used where it is permissible to use a SQLite `?` placeholder. You can use it only for values in SQL statements, but not for table names or column names or any other non-value context. This method also cannot be used in conjunction with `pragma` statements and the like. Second, note the lack of quotation marks in the SQL. The `WHERE` clause was _not_ `WHERE name='%@'` (like you might have to do if you built a SQL statement using `NSString` method `stringWithFormat`), but rather simply `WHERE name=%@`. - - */ - -- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2); - -/** Execute select statement - - Executing queries returns an `` object if successful, and `nil` upon failure. Like executing updates, there is a variant that accepts an `NSError **` parameter. Otherwise you should use the `` and `` methods to determine why a query failed. - - In order to iterate through the results of your query, you use a `while()` loop. You also need to "step" (via `<[FMResultSet next]>`) from one record to the other. - - @param sql The SELECT statement to be performed, with optional `?` placeholders. - - @param arguments A `NSArray` of objects to be used when binding values to the `?` placeholders in the SQL statement. - - @return A `` for the result set upon success; `nil` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see -executeQuery:values:error: - @see FMResultSet - @see [`FMResultSet next`](<[FMResultSet next]>) - */ - -- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments; - -/** Execute select statement - - Executing queries returns an `` object if successful, and `nil` upon failure. Like executing updates, there is a variant that accepts an `NSError **` parameter. Otherwise you should use the `` and `` methods to determine why a query failed. - - In order to iterate through the results of your query, you use a `while()` loop. You also need to "step" (via `<[FMResultSet next]>`) from one record to the other. - - This is similar to ``, except that this also accepts a pointer to a `NSError` pointer, so that errors can be returned. - - In Swift 2, this throws errors, as if it were defined as follows: - - `func executeQuery(sql: String!, values: [AnyObject]!) throws -> FMResultSet!` - - @param sql The SELECT statement to be performed, with optional `?` placeholders. - - @param values A `NSArray` of objects to be used when binding values to the `?` placeholders in the SQL statement. - - @param error A `NSError` object to receive any error object (if any). - - @return A `` for the result set upon success; `nil` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see FMResultSet - @see [`FMResultSet next`](<[FMResultSet next]>) - - @note When called from Swift, only use the first two parameters, `sql` and `values`. This but throws the error. - - */ - -- (FMResultSet *)executeQuery:(NSString *)sql values:(NSArray *)values error:(NSError * __autoreleasing *)error; - -/** Execute select statement - - Executing queries returns an `` object if successful, and `nil` upon failure. Like executing updates, there is a variant that accepts an `NSError **` parameter. Otherwise you should use the `` and `` methods to determine why a query failed. - - In order to iterate through the results of your query, you use a `while()` loop. You also need to "step" (via `<[FMResultSet next]>`) from one record to the other. - - @param sql The SELECT statement to be performed, with optional `?` placeholders. - - @param arguments A `NSDictionary` of objects keyed by column names that will be used when binding values to the `?` placeholders in the SQL statement. - - @return A `` for the result set upon success; `nil` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see FMResultSet - @see [`FMResultSet next`](<[FMResultSet next]>) - */ - -- (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments; - - -// Documentation forthcoming. -- (FMResultSet *)executeQuery:(NSString*)sql withVAList: (va_list)args; - -///------------------- -/// @name Transactions -///------------------- - -/** Begin a transaction - - @return `YES` on success; `NO` on failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see commit - @see rollback - @see beginDeferredTransaction - @see inTransaction - */ - -- (BOOL)beginTransaction; - -/** Begin a deferred transaction - - @return `YES` on success; `NO` on failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see commit - @see rollback - @see beginTransaction - @see inTransaction - */ - -- (BOOL)beginDeferredTransaction; - -/** Commit a transaction - - Commit a transaction that was initiated with either `` or with ``. - - @return `YES` on success; `NO` on failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see beginTransaction - @see beginDeferredTransaction - @see rollback - @see inTransaction - */ - -- (BOOL)commit; - -/** Rollback a transaction - - Rollback a transaction that was initiated with either `` or with ``. - - @return `YES` on success; `NO` on failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see beginTransaction - @see beginDeferredTransaction - @see commit - @see inTransaction - */ - -- (BOOL)rollback; - -/** Identify whether currently in a transaction or not - - @return `YES` if currently within transaction; `NO` if not. - - @see beginTransaction - @see beginDeferredTransaction - @see commit - @see rollback - */ - -- (BOOL)inTransaction; - - -///---------------------------------------- -/// @name Cached statements and result sets -///---------------------------------------- - -/** Clear cached statements */ - -- (void)clearCachedStatements; - -/** Close all open result sets */ - -- (void)closeOpenResultSets; - -/** Whether database has any open result sets - - @return `YES` if there are open result sets; `NO` if not. - */ - -- (BOOL)hasOpenResultSets; - -/** Return whether should cache statements or not - - @return `YES` if should cache statements; `NO` if not. - */ - -- (BOOL)shouldCacheStatements; - -/** Set whether should cache statements or not - - @param value `YES` if should cache statements; `NO` if not. - */ - -- (void)setShouldCacheStatements:(BOOL)value; - - -///------------------------- -/// @name Encryption methods -///------------------------- - -/** Set encryption key. - - @param key The key to be used. - - @return `YES` if success, `NO` on error. - - @see https://www.zetetic.net/sqlcipher/ - - @warning You need to have purchased the sqlite encryption extensions for this method to work. - */ - -- (BOOL)setKey:(NSString*)key; - -/** Reset encryption key - - @param key The key to be used. - - @return `YES` if success, `NO` on error. - - @see https://www.zetetic.net/sqlcipher/ - - @warning You need to have purchased the sqlite encryption extensions for this method to work. - */ - -- (BOOL)rekey:(NSString*)key; - -/** Set encryption key using `keyData`. - - @param keyData The `NSData` to be used. - - @return `YES` if success, `NO` on error. - - @see https://www.zetetic.net/sqlcipher/ - - @warning You need to have purchased the sqlite encryption extensions for this method to work. - */ - -- (BOOL)setKeyWithData:(NSData *)keyData; - -/** Reset encryption key using `keyData`. - - @param keyData The `NSData` to be used. - - @return `YES` if success, `NO` on error. - - @see https://www.zetetic.net/sqlcipher/ - - @warning You need to have purchased the sqlite encryption extensions for this method to work. - */ - -- (BOOL)rekeyWithData:(NSData *)keyData; - - -///------------------------------ -/// @name General inquiry methods -///------------------------------ - -/** The path of the database file - - @return path of database. - - */ - -- (NSString *)databasePath; - -/** The underlying SQLite handle - - @return The `sqlite3` pointer. - - */ - -- (void*)sqliteHandle; - - -///----------------------------- -/// @name Retrieving error codes -///----------------------------- - -/** Last error message - - Returns the English-language text that describes the most recent failed SQLite API call associated with a database connection. If a prior API call failed but the most recent API call succeeded, this return value is undefined. - - @return `NSString` of the last error message. - - @see [sqlite3_errmsg()](http://sqlite.org/c3ref/errcode.html) - @see lastErrorCode - @see lastError - - */ - -- (NSString*)lastErrorMessage; - -/** Last error code - - Returns the numeric result code or extended result code for the most recent failed SQLite API call associated with a database connection. If a prior API call failed but the most recent API call succeeded, this return value is undefined. - - @return Integer value of the last error code. - - @see [sqlite3_errcode()](http://sqlite.org/c3ref/errcode.html) - @see lastErrorMessage - @see lastError - - */ - -- (int)lastErrorCode; - -/** Had error - - @return `YES` if there was an error, `NO` if no error. - - @see lastError - @see lastErrorCode - @see lastErrorMessage - - */ - -- (BOOL)hadError; - -/** Last error - - @return `NSError` representing the last error. - - @see lastErrorCode - @see lastErrorMessage - - */ - -- (NSError*)lastError; - - -// description forthcoming -- (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeoutInSeconds; -- (NSTimeInterval)maxBusyRetryTimeInterval; - - -///------------------ -/// @name Save points -///------------------ - -/** Start save point - - @param name Name of save point. - - @param outErr A `NSError` object to receive any error object (if any). - - @return `YES` on success; `NO` on failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see releaseSavePointWithName:error: - @see rollbackToSavePointWithName:error: - */ - -- (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr; - -/** Release save point - - @param name Name of save point. - - @param outErr A `NSError` object to receive any error object (if any). - - @return `YES` on success; `NO` on failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see startSavePointWithName:error: - @see rollbackToSavePointWithName:error: - - */ - -- (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr; - -/** Roll back to save point - - @param name Name of save point. - @param outErr A `NSError` object to receive any error object (if any). - - @return `YES` on success; `NO` on failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - - @see startSavePointWithName:error: - @see releaseSavePointWithName:error: - - */ - -- (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr; - -/** Start save point - - @param block Block of code to perform from within save point. - - @return The NSError corresponding to the error, if any. If no error, returns `nil`. - - @see startSavePointWithName:error: - @see releaseSavePointWithName:error: - @see rollbackToSavePointWithName:error: - - */ - -- (NSError*)inSavePoint:(void (^)(BOOL *rollback))block; - -///---------------------------- -/// @name SQLite library status -///---------------------------- - -/** Test to see if the library is threadsafe - - @return `NO` if and only if SQLite was compiled with mutexing code omitted due to the SQLITE_THREADSAFE compile-time option being set to 0. - - @see [sqlite3_threadsafe()](http://sqlite.org/c3ref/threadsafe.html) - */ - -+ (BOOL)isSQLiteThreadSafe; - -/** Run-time library version numbers - - @return The sqlite library version string. - - @see [sqlite3_libversion()](http://sqlite.org/c3ref/libversion.html) - */ - -+ (NSString*)sqliteLibVersion; - - -+ (NSString*)FMDBUserVersion; - -+ (SInt32)FMDBVersion; - - -///------------------------ -/// @name Make SQL function -///------------------------ - -/** Adds SQL functions or aggregates or to redefine the behavior of existing SQL functions or aggregates. - - For example: - - [queue inDatabase:^(FMDatabase *adb) { - - [adb executeUpdate:@"create table ftest (foo text)"]; - [adb executeUpdate:@"insert into ftest values ('hello')"]; - [adb executeUpdate:@"insert into ftest values ('hi')"]; - [adb executeUpdate:@"insert into ftest values ('not h!')"]; - [adb executeUpdate:@"insert into ftest values ('definitely not h!')"]; - - [adb makeFunctionNamed:@"StringStartsWithH" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) { - if (sqlite3_value_type(aargv[0]) == SQLITE_TEXT) { - @autoreleasepool { - const char *c = (const char *)sqlite3_value_text(aargv[0]); - NSString *s = [NSString stringWithUTF8String:c]; - sqlite3_result_int(context, [s hasPrefix:@"h"]); - } - } - else { - NSLog(@"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__); - sqlite3_result_null(context); - } - }]; - - int rowCount = 0; - FMResultSet *ars = [adb executeQuery:@"select * from ftest where StringStartsWithH(foo)"]; - while ([ars next]) { - rowCount++; - NSLog(@"Does %@ start with 'h'?", [rs stringForColumnIndex:0]); - } - FMDBQuickCheck(rowCount == 2); - }]; - - @param name Name of function - - @param count Maximum number of parameters - - @param block The block of code for the function - - @see [sqlite3_create_function()](http://sqlite.org/c3ref/create_function.html) - */ - -- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(void *context, int argc, void **argv))block; - - -///--------------------- -/// @name Date formatter -///--------------------- - -/** Generate an `NSDateFormatter` that won't be broken by permutations of timezones or locales. - - Use this method to generate values to set the dateFormat property. - - Example: - - myDB.dateFormat = [FMDatabase storeableDateFormat:@"yyyy-MM-dd HH:mm:ss"]; - - @param format A valid NSDateFormatter format string. - - @return A `NSDateFormatter` that can be used for converting dates to strings and vice versa. - - @see hasDateFormatter - @see setDateFormat: - @see dateFromString: - @see stringFromDate: - @see storeableDateFormat: - - @warning Note that `NSDateFormatter` is not thread-safe, so the formatter generated by this method should be assigned to only one FMDB instance and should not be used for other purposes. - - */ - -+ (NSDateFormatter *)storeableDateFormat:(NSString *)format; - -/** Test whether the database has a date formatter assigned. - - @return `YES` if there is a date formatter; `NO` if not. - - @see hasDateFormatter - @see setDateFormat: - @see dateFromString: - @see stringFromDate: - @see storeableDateFormat: - */ - -- (BOOL)hasDateFormatter; - -/** Set to a date formatter to use string dates with sqlite instead of the default UNIX timestamps. - - @param format Set to nil to use UNIX timestamps. Defaults to nil. Should be set using a formatter generated using FMDatabase::storeableDateFormat. - - @see hasDateFormatter - @see setDateFormat: - @see dateFromString: - @see stringFromDate: - @see storeableDateFormat: - - @warning Note there is no direct getter for the `NSDateFormatter`, and you should not use the formatter you pass to FMDB for other purposes, as `NSDateFormatter` is not thread-safe. - */ - -- (void)setDateFormat:(NSDateFormatter *)format; - -/** Convert the supplied NSString to NSDate, using the current database formatter. - - @param s `NSString` to convert to `NSDate`. - - @return The `NSDate` object; or `nil` if no formatter is set. - - @see hasDateFormatter - @see setDateFormat: - @see dateFromString: - @see stringFromDate: - @see storeableDateFormat: - */ - -- (NSDate *)dateFromString:(NSString *)s; - -/** Convert the supplied NSDate to NSString, using the current database formatter. - - @param date `NSDate` of date to convert to `NSString`. - - @return The `NSString` representation of the date; `nil` if no formatter is set. - - @see hasDateFormatter - @see setDateFormat: - @see dateFromString: - @see stringFromDate: - @see storeableDateFormat: - */ - -- (NSString *)stringFromDate:(NSDate *)date; - -@end - - -/** Objective-C wrapper for `sqlite3_stmt` - - This is a wrapper for a SQLite `sqlite3_stmt`. Generally when using FMDB you will not need to interact directly with `FMStatement`, but rather with `` and `` only. - - ### See also - - - `` - - `` - - [`sqlite3_stmt`](http://www.sqlite.org/c3ref/stmt.html) - */ - -@interface FMStatement : NSObject { - void *_statement; - NSString *_query; - long _useCount; - BOOL _inUse; -} - -///----------------- -/// @name Properties -///----------------- - -/** Usage count */ - -@property (atomic, assign) long useCount; - -/** SQL statement */ - -@property (atomic, retain) NSString *query; - -/** SQLite sqlite3_stmt - - @see [`sqlite3_stmt`](http://www.sqlite.org/c3ref/stmt.html) - */ - -@property (atomic, assign) void *statement; - -/** Indication of whether the statement is in use */ - -@property (atomic, assign) BOOL inUse; - -///---------------------------- -/// @name Closing and Resetting -///---------------------------- - -/** Close statement */ - -- (void)close; - -/** Reset statement */ - -- (void)reset; - -@end - -#pragma clang diagnostic pop - diff --git a/Pods/FMDB/src/fmdb/FMDatabase.m b/Pods/FMDB/src/fmdb/FMDatabase.m deleted file mode 100644 index d33c13d..0000000 --- a/Pods/FMDB/src/fmdb/FMDatabase.m +++ /dev/null @@ -1,1479 +0,0 @@ -#import "FMDatabase.h" -#import "unistd.h" -#import - -#if FMDB_SQLITE_STANDALONE -#import -#else -#import -#endif - -@interface FMDatabase () - -- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; -- (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; - -@end - -@implementation FMDatabase -@synthesize cachedStatements=_cachedStatements; -@synthesize logsErrors=_logsErrors; -@synthesize crashOnErrors=_crashOnErrors; -@synthesize checkedOut=_checkedOut; -@synthesize traceExecution=_traceExecution; - -#pragma mark FMDatabase instantiation and deallocation - -+ (instancetype)databaseWithPath:(NSString*)aPath { - return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]); -} - -- (instancetype)init { - return [self initWithPath:nil]; -} - -- (instancetype)initWithPath:(NSString*)aPath { - - assert(sqlite3_threadsafe()); // whoa there big boy- gotta make sure sqlite it happy with what we're going to do. - - self = [super init]; - - if (self) { - _databasePath = [aPath copy]; - _openResultSets = [[NSMutableSet alloc] init]; - _db = nil; - _logsErrors = YES; - _crashOnErrors = NO; - _maxBusyRetryTimeInterval = 2; - } - - return self; -} - -- (void)finalize { - [self close]; - [super finalize]; -} - -- (void)dealloc { - [self close]; - FMDBRelease(_openResultSets); - FMDBRelease(_cachedStatements); - FMDBRelease(_dateFormat); - FMDBRelease(_databasePath); - FMDBRelease(_openFunctions); - -#if ! __has_feature(objc_arc) - [super dealloc]; -#endif -} - -- (NSString *)databasePath { - return _databasePath; -} - -+ (NSString*)FMDBUserVersion { - return @"2.6.2"; -} - -// returns 0x0240 for version 2.4. This makes it super easy to do things like: -// /* need to make sure to do X with FMDB version 2.4 or later */ -// if ([FMDatabase FMDBVersion] >= 0x0240) { … } - -+ (SInt32)FMDBVersion { - - // we go through these hoops so that we only have to change the version number in a single spot. - static dispatch_once_t once; - static SInt32 FMDBVersionVal = 0; - - dispatch_once(&once, ^{ - NSString *prodVersion = [self FMDBUserVersion]; - - if ([[prodVersion componentsSeparatedByString:@"."] count] < 3) { - prodVersion = [prodVersion stringByAppendingString:@".0"]; - } - - NSString *junk = [prodVersion stringByReplacingOccurrencesOfString:@"." withString:@""]; - - char *e = nil; - FMDBVersionVal = (int) strtoul([junk UTF8String], &e, 16); - - }); - - return FMDBVersionVal; -} - -#pragma mark SQLite information - -+ (NSString*)sqliteLibVersion { - return [NSString stringWithFormat:@"%s", sqlite3_libversion()]; -} - -+ (BOOL)isSQLiteThreadSafe { - // make sure to read the sqlite headers on this guy! - return sqlite3_threadsafe() != 0; -} - -- (void*)sqliteHandle { - return _db; -} - -- (const char*)sqlitePath { - - if (!_databasePath) { - return ":memory:"; - } - - if ([_databasePath length] == 0) { - return ""; // this creates a temporary database (it's an sqlite thing). - } - - return [_databasePath fileSystemRepresentation]; - -} - -#pragma mark Open and close database - -- (BOOL)open { - if (_db) { - return YES; - } - - int err = sqlite3_open([self sqlitePath], (sqlite3**)&_db ); - if(err != SQLITE_OK) { - NSLog(@"error opening!: %d", err); - return NO; - } - - if (_maxBusyRetryTimeInterval > 0.0) { - // set the handler - [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval]; - } - - - return YES; -} - -- (BOOL)openWithFlags:(int)flags { - return [self openWithFlags:flags vfs:nil]; -} -- (BOOL)openWithFlags:(int)flags vfs:(NSString *)vfsName { -#if SQLITE_VERSION_NUMBER >= 3005000 - if (_db) { - return YES; - } - - int err = sqlite3_open_v2([self sqlitePath], (sqlite3**)&_db, flags, [vfsName UTF8String]); - if(err != SQLITE_OK) { - NSLog(@"error opening!: %d", err); - return NO; - } - - if (_maxBusyRetryTimeInterval > 0.0) { - // set the handler - [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval]; - } - - return YES; -#else - NSLog(@"openWithFlags requires SQLite 3.5"); - return NO; -#endif -} - - -- (BOOL)close { - - [self clearCachedStatements]; - [self closeOpenResultSets]; - - if (!_db) { - return YES; - } - - int rc; - BOOL retry; - BOOL triedFinalizingOpenStatements = NO; - - do { - retry = NO; - rc = sqlite3_close(_db); - if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { - if (!triedFinalizingOpenStatements) { - triedFinalizingOpenStatements = YES; - sqlite3_stmt *pStmt; - while ((pStmt = sqlite3_next_stmt(_db, nil)) !=0) { - NSLog(@"Closing leaked statement"); - sqlite3_finalize(pStmt); - retry = YES; - } - } - } - else if (SQLITE_OK != rc) { - NSLog(@"error closing!: %d", rc); - } - } - while (retry); - - _db = nil; - return YES; -} - -#pragma mark Busy handler routines - -// NOTE: appledoc seems to choke on this function for some reason; -// so when generating documentation, you might want to ignore the -// .m files so that it only documents the public interfaces outlined -// in the .h files. -// -// This is a known appledoc bug that it has problems with C functions -// within a class implementation, but for some reason, only this -// C function causes problems; the rest don't. Anyway, ignoring the .m -// files with appledoc will prevent this problem from occurring. - -static int FMDBDatabaseBusyHandler(void *f, int count) { - FMDatabase *self = (__bridge FMDatabase*)f; - - if (count == 0) { - self->_startBusyRetryTime = [NSDate timeIntervalSinceReferenceDate]; - return 1; - } - - NSTimeInterval delta = [NSDate timeIntervalSinceReferenceDate] - (self->_startBusyRetryTime); - - if (delta < [self maxBusyRetryTimeInterval]) { - int requestedSleepInMillseconds = (int) arc4random_uniform(50) + 50; - int actualSleepInMilliseconds = sqlite3_sleep(requestedSleepInMillseconds); - if (actualSleepInMilliseconds != requestedSleepInMillseconds) { - NSLog(@"WARNING: Requested sleep of %i milliseconds, but SQLite returned %i. Maybe SQLite wasn't built with HAVE_USLEEP=1?", requestedSleepInMillseconds, actualSleepInMilliseconds); - } - return 1; - } - - return 0; -} - -- (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeout { - - _maxBusyRetryTimeInterval = timeout; - - if (!_db) { - return; - } - - if (timeout > 0) { - sqlite3_busy_handler(_db, &FMDBDatabaseBusyHandler, (__bridge void *)(self)); - } - else { - // turn it off otherwise - sqlite3_busy_handler(_db, nil, nil); - } -} - -- (NSTimeInterval)maxBusyRetryTimeInterval { - return _maxBusyRetryTimeInterval; -} - - -// we no longer make busyRetryTimeout public -// but for folks who don't bother noticing that the interface to FMDatabase changed, -// we'll still implement the method so they don't get suprise crashes -- (int)busyRetryTimeout { - NSLog(@"%s:%d", __FUNCTION__, __LINE__); - NSLog(@"FMDB: busyRetryTimeout no longer works, please use maxBusyRetryTimeInterval"); - return -1; -} - -- (void)setBusyRetryTimeout:(int)i { -#pragma unused(i) - NSLog(@"%s:%d", __FUNCTION__, __LINE__); - NSLog(@"FMDB: setBusyRetryTimeout does nothing, please use setMaxBusyRetryTimeInterval:"); -} - -#pragma mark Result set functions - -- (BOOL)hasOpenResultSets { - return [_openResultSets count] > 0; -} - -- (void)closeOpenResultSets { - - //Copy the set so we don't get mutation errors - NSSet *openSetCopy = FMDBReturnAutoreleased([_openResultSets copy]); - for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) { - FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue]; - - [rs setParentDB:nil]; - [rs close]; - - [_openResultSets removeObject:rsInWrappedInATastyValueMeal]; - } -} - -- (void)resultSetDidClose:(FMResultSet *)resultSet { - NSValue *setValue = [NSValue valueWithNonretainedObject:resultSet]; - - [_openResultSets removeObject:setValue]; -} - -#pragma mark Cached statements - -- (void)clearCachedStatements { - - for (NSMutableSet *statements in [_cachedStatements objectEnumerator]) { - [statements makeObjectsPerformSelector:@selector(close)]; - } - - [_cachedStatements removeAllObjects]; -} - -- (FMStatement*)cachedStatementForQuery:(NSString*)query { - - NSMutableSet* statements = [_cachedStatements objectForKey:query]; - - return [[statements objectsPassingTest:^BOOL(FMStatement* statement, BOOL *stop) { - - *stop = ![statement inUse]; - return *stop; - - }] anyObject]; -} - - -- (void)setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query { - - query = [query copy]; // in case we got handed in a mutable string... - [statement setQuery:query]; - - NSMutableSet* statements = [_cachedStatements objectForKey:query]; - if (!statements) { - statements = [NSMutableSet set]; - } - - [statements addObject:statement]; - - [_cachedStatements setObject:statements forKey:query]; - - FMDBRelease(query); -} - -#pragma mark Key routines - -- (BOOL)rekey:(NSString*)key { - NSData *keyData = [NSData dataWithBytes:(void *)[key UTF8String] length:(NSUInteger)strlen([key UTF8String])]; - - return [self rekeyWithData:keyData]; -} - -- (BOOL)rekeyWithData:(NSData *)keyData { -#ifdef SQLITE_HAS_CODEC - if (!keyData) { - return NO; - } - - int rc = sqlite3_rekey(_db, [keyData bytes], (int)[keyData length]); - - if (rc != SQLITE_OK) { - NSLog(@"error on rekey: %d", rc); - NSLog(@"%@", [self lastErrorMessage]); - } - - return (rc == SQLITE_OK); -#else -#pragma unused(keyData) - return NO; -#endif -} - -- (BOOL)setKey:(NSString*)key { - NSData *keyData = [NSData dataWithBytes:[key UTF8String] length:(NSUInteger)strlen([key UTF8String])]; - - return [self setKeyWithData:keyData]; -} - -- (BOOL)setKeyWithData:(NSData *)keyData { -#ifdef SQLITE_HAS_CODEC - if (!keyData) { - return NO; - } - - int rc = sqlite3_key(_db, [keyData bytes], (int)[keyData length]); - - return (rc == SQLITE_OK); -#else -#pragma unused(keyData) - return NO; -#endif -} - -#pragma mark Date routines - -+ (NSDateFormatter *)storeableDateFormat:(NSString *)format { - - NSDateFormatter *result = FMDBReturnAutoreleased([[NSDateFormatter alloc] init]); - result.dateFormat = format; - result.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; - result.locale = FMDBReturnAutoreleased([[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]); - return result; -} - - -- (BOOL)hasDateFormatter { - return _dateFormat != nil; -} - -- (void)setDateFormat:(NSDateFormatter *)format { - FMDBAutorelease(_dateFormat); - _dateFormat = FMDBReturnRetained(format); -} - -- (NSDate *)dateFromString:(NSString *)s { - return [_dateFormat dateFromString:s]; -} - -- (NSString *)stringFromDate:(NSDate *)date { - return [_dateFormat stringFromDate:date]; -} - -#pragma mark State of database - -- (BOOL)goodConnection { - - if (!_db) { - return NO; - } - - FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"]; - - if (rs) { - [rs close]; - return YES; - } - - return NO; -} - -- (void)warnInUse { - NSLog(@"The FMDatabase %@ is currently in use.", self); - -#ifndef NS_BLOCK_ASSERTIONS - if (_crashOnErrors) { - NSAssert(false, @"The FMDatabase %@ is currently in use.", self); - abort(); - } -#endif -} - -- (BOOL)databaseExists { - - if (!_db) { - - NSLog(@"The FMDatabase %@ is not open.", self); - -#ifndef NS_BLOCK_ASSERTIONS - if (_crashOnErrors) { - NSAssert(false, @"The FMDatabase %@ is not open.", self); - abort(); - } -#endif - - return NO; - } - - return YES; -} - -#pragma mark Error routines - -- (NSString*)lastErrorMessage { - return [NSString stringWithUTF8String:sqlite3_errmsg(_db)]; -} - -- (BOOL)hadError { - int lastErrCode = [self lastErrorCode]; - - return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW); -} - -- (int)lastErrorCode { - return sqlite3_errcode(_db); -} - -- (NSError*)errorWithMessage:(NSString*)message { - NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:@"FMDatabase" code:sqlite3_errcode(_db) userInfo:errorMessage]; -} - -- (NSError*)lastError { - return [self errorWithMessage:[self lastErrorMessage]]; -} - -#pragma mark Update information routines - -- (sqlite_int64)lastInsertRowId { - - if (_isExecutingStatement) { - [self warnInUse]; - return NO; - } - - _isExecutingStatement = YES; - - sqlite_int64 ret = sqlite3_last_insert_rowid(_db); - - _isExecutingStatement = NO; - - return ret; -} - -- (int)changes { - if (_isExecutingStatement) { - [self warnInUse]; - return 0; - } - - _isExecutingStatement = YES; - - int ret = sqlite3_changes(_db); - - _isExecutingStatement = NO; - - return ret; -} - -#pragma mark SQL manipulation - -- (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt { - - if ((!obj) || ((NSNull *)obj == [NSNull null])) { - sqlite3_bind_null(pStmt, idx); - } - - // FIXME - someday check the return codes on these binds. - else if ([obj isKindOfClass:[NSData class]]) { - const void *bytes = [obj bytes]; - if (!bytes) { - // it's an empty NSData object, aka [NSData data]. - // Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob. - bytes = ""; - } - sqlite3_bind_blob(pStmt, idx, bytes, (int)[obj length], SQLITE_STATIC); - } - else if ([obj isKindOfClass:[NSDate class]]) { - if (self.hasDateFormatter) - sqlite3_bind_text(pStmt, idx, [[self stringFromDate:obj] UTF8String], -1, SQLITE_STATIC); - else - sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]); - } - else if ([obj isKindOfClass:[NSNumber class]]) { - - if (strcmp([obj objCType], @encode(char)) == 0) { - sqlite3_bind_int(pStmt, idx, [obj charValue]); - } - else if (strcmp([obj objCType], @encode(unsigned char)) == 0) { - sqlite3_bind_int(pStmt, idx, [obj unsignedCharValue]); - } - else if (strcmp([obj objCType], @encode(short)) == 0) { - sqlite3_bind_int(pStmt, idx, [obj shortValue]); - } - else if (strcmp([obj objCType], @encode(unsigned short)) == 0) { - sqlite3_bind_int(pStmt, idx, [obj unsignedShortValue]); - } - else if (strcmp([obj objCType], @encode(int)) == 0) { - sqlite3_bind_int(pStmt, idx, [obj intValue]); - } - else if (strcmp([obj objCType], @encode(unsigned int)) == 0) { - sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedIntValue]); - } - else if (strcmp([obj objCType], @encode(long)) == 0) { - sqlite3_bind_int64(pStmt, idx, [obj longValue]); - } - else if (strcmp([obj objCType], @encode(unsigned long)) == 0) { - sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongValue]); - } - else if (strcmp([obj objCType], @encode(long long)) == 0) { - sqlite3_bind_int64(pStmt, idx, [obj longLongValue]); - } - else if (strcmp([obj objCType], @encode(unsigned long long)) == 0) { - sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongLongValue]); - } - else if (strcmp([obj objCType], @encode(float)) == 0) { - sqlite3_bind_double(pStmt, idx, [obj floatValue]); - } - else if (strcmp([obj objCType], @encode(double)) == 0) { - sqlite3_bind_double(pStmt, idx, [obj doubleValue]); - } - else if (strcmp([obj objCType], @encode(BOOL)) == 0) { - sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0)); - } - else { - sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); - } - } - else { - sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); - } -} - -- (void)extractSQL:(NSString *)sql argumentsList:(va_list)args intoString:(NSMutableString *)cleanedSQL arguments:(NSMutableArray *)arguments { - - NSUInteger length = [sql length]; - unichar last = '\0'; - for (NSUInteger i = 0; i < length; ++i) { - id arg = nil; - unichar current = [sql characterAtIndex:i]; - unichar add = current; - if (last == '%') { - switch (current) { - case '@': - arg = va_arg(args, id); - break; - case 'c': - // warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg has undefined behavior because arguments will be promoted to 'int' - arg = [NSString stringWithFormat:@"%c", va_arg(args, int)]; - break; - case 's': - arg = [NSString stringWithUTF8String:va_arg(args, char*)]; - break; - case 'd': - case 'D': - case 'i': - arg = [NSNumber numberWithInt:va_arg(args, int)]; - break; - case 'u': - case 'U': - arg = [NSNumber numberWithUnsignedInt:va_arg(args, unsigned int)]; - break; - case 'h': - i++; - if (i < length && [sql characterAtIndex:i] == 'i') { - // warning: second argument to 'va_arg' is of promotable type 'short'; this va_arg has undefined behavior because arguments will be promoted to 'int' - arg = [NSNumber numberWithShort:(short)(va_arg(args, int))]; - } - else if (i < length && [sql characterAtIndex:i] == 'u') { - // warning: second argument to 'va_arg' is of promotable type 'unsigned short'; this va_arg has undefined behavior because arguments will be promoted to 'int' - arg = [NSNumber numberWithUnsignedShort:(unsigned short)(va_arg(args, uint))]; - } - else { - i--; - } - break; - case 'q': - i++; - if (i < length && [sql characterAtIndex:i] == 'i') { - arg = [NSNumber numberWithLongLong:va_arg(args, long long)]; - } - else if (i < length && [sql characterAtIndex:i] == 'u') { - arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)]; - } - else { - i--; - } - break; - case 'f': - arg = [NSNumber numberWithDouble:va_arg(args, double)]; - break; - case 'g': - // warning: second argument to 'va_arg' is of promotable type 'float'; this va_arg has undefined behavior because arguments will be promoted to 'double' - arg = [NSNumber numberWithFloat:(float)(va_arg(args, double))]; - break; - case 'l': - i++; - if (i < length) { - unichar next = [sql characterAtIndex:i]; - if (next == 'l') { - i++; - if (i < length && [sql characterAtIndex:i] == 'd') { - //%lld - arg = [NSNumber numberWithLongLong:va_arg(args, long long)]; - } - else if (i < length && [sql characterAtIndex:i] == 'u') { - //%llu - arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)]; - } - else { - i--; - } - } - else if (next == 'd') { - //%ld - arg = [NSNumber numberWithLong:va_arg(args, long)]; - } - else if (next == 'u') { - //%lu - arg = [NSNumber numberWithUnsignedLong:va_arg(args, unsigned long)]; - } - else { - i--; - } - } - else { - i--; - } - break; - default: - // something else that we can't interpret. just pass it on through like normal - break; - } - } - else if (current == '%') { - // percent sign; skip this character - add = '\0'; - } - - if (arg != nil) { - [cleanedSQL appendString:@"?"]; - [arguments addObject:arg]; - } - else if (add == (unichar)'@' && last == (unichar) '%') { - [cleanedSQL appendFormat:@"NULL"]; - } - else if (add != '\0') { - [cleanedSQL appendFormat:@"%C", add]; - } - last = current; - } -} - -#pragma mark Execute queries - -- (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments { - return [self executeQuery:sql withArgumentsInArray:nil orDictionary:arguments orVAList:nil]; -} - -- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args { - - if (![self databaseExists]) { - return 0x00; - } - - if (_isExecutingStatement) { - [self warnInUse]; - return 0x00; - } - - _isExecutingStatement = YES; - - int rc = 0x00; - sqlite3_stmt *pStmt = 0x00; - FMStatement *statement = 0x00; - FMResultSet *rs = 0x00; - - if (_traceExecution && sql) { - NSLog(@"%@ executeQuery: %@", self, sql); - } - - if (_shouldCacheStatements) { - statement = [self cachedStatementForQuery:sql]; - pStmt = statement ? [statement statement] : 0x00; - [statement reset]; - } - - if (!pStmt) { - - rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); - - if (SQLITE_OK != rc) { - if (_logsErrors) { - NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); - NSLog(@"DB Query: %@", sql); - NSLog(@"DB Path: %@", _databasePath); - } - - if (_crashOnErrors) { - NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); - abort(); - } - - sqlite3_finalize(pStmt); - _isExecutingStatement = NO; - return nil; - } - } - - id obj; - int idx = 0; - int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!) - - // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support - if (dictionaryArgs) { - - for (NSString *dictionaryKey in [dictionaryArgs allKeys]) { - - // Prefix the key with a colon. - NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey]; - - if (_traceExecution) { - NSLog(@"%@ = %@", parameterName, [dictionaryArgs objectForKey:dictionaryKey]); - } - - // Get the index for the parameter name. - int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]); - - FMDBRelease(parameterName); - - if (namedIdx > 0) { - // Standard binding from here. - [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt]; - // increment the binding count, so our check below works out - idx++; - } - else { - NSLog(@"Could not find index for %@", dictionaryKey); - } - } - } - else { - - while (idx < queryCount) { - - if (arrayArgs && idx < (int)[arrayArgs count]) { - obj = [arrayArgs objectAtIndex:(NSUInteger)idx]; - } - else if (args) { - obj = va_arg(args, id); - } - else { - //We ran out of arguments - break; - } - - if (_traceExecution) { - if ([obj isKindOfClass:[NSData class]]) { - NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]); - } - else { - NSLog(@"obj: %@", obj); - } - } - - idx++; - - [self bindObject:obj toColumn:idx inStatement:pStmt]; - } - } - - if (idx != queryCount) { - NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)"); - sqlite3_finalize(pStmt); - _isExecutingStatement = NO; - return nil; - } - - FMDBRetain(statement); // to balance the release below - - if (!statement) { - statement = [[FMStatement alloc] init]; - [statement setStatement:pStmt]; - - if (_shouldCacheStatements && sql) { - [self setCachedStatement:statement forQuery:sql]; - } - } - - // the statement gets closed in rs's dealloc or [rs close]; - rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self]; - [rs setQuery:sql]; - - NSValue *openResultSet = [NSValue valueWithNonretainedObject:rs]; - [_openResultSets addObject:openResultSet]; - - [statement setUseCount:[statement useCount] + 1]; - - FMDBRelease(statement); - - _isExecutingStatement = NO; - - return rs; -} - -- (FMResultSet *)executeQuery:(NSString*)sql, ... { - va_list args; - va_start(args, sql); - - id result = [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args]; - - va_end(args); - return result; -} - -- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... { - va_list args; - va_start(args, format); - - NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]]; - NSMutableArray *arguments = [NSMutableArray array]; - [self extractSQL:format argumentsList:args intoString:sql arguments:arguments]; - - va_end(args); - - return [self executeQuery:sql withArgumentsInArray:arguments]; -} - -- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments { - return [self executeQuery:sql withArgumentsInArray:arguments orDictionary:nil orVAList:nil]; -} - -- (FMResultSet *)executeQuery:(NSString *)sql values:(NSArray *)values error:(NSError * __autoreleasing *)error { - FMResultSet *rs = [self executeQuery:sql withArgumentsInArray:values orDictionary:nil orVAList:nil]; - if (!rs && error) { - *error = [self lastError]; - } - return rs; -} - -- (FMResultSet *)executeQuery:(NSString*)sql withVAList:(va_list)args { - return [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args]; -} - -#pragma mark Execute updates - -- (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args { - - if (![self databaseExists]) { - return NO; - } - - if (_isExecutingStatement) { - [self warnInUse]; - return NO; - } - - _isExecutingStatement = YES; - - int rc = 0x00; - sqlite3_stmt *pStmt = 0x00; - FMStatement *cachedStmt = 0x00; - - if (_traceExecution && sql) { - NSLog(@"%@ executeUpdate: %@", self, sql); - } - - if (_shouldCacheStatements) { - cachedStmt = [self cachedStatementForQuery:sql]; - pStmt = cachedStmt ? [cachedStmt statement] : 0x00; - [cachedStmt reset]; - } - - if (!pStmt) { - rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); - - if (SQLITE_OK != rc) { - if (_logsErrors) { - NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); - NSLog(@"DB Query: %@", sql); - NSLog(@"DB Path: %@", _databasePath); - } - - if (_crashOnErrors) { - NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); - abort(); - } - - if (outErr) { - *outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]]; - } - - sqlite3_finalize(pStmt); - - _isExecutingStatement = NO; - return NO; - } - } - - id obj; - int idx = 0; - int queryCount = sqlite3_bind_parameter_count(pStmt); - - // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support - if (dictionaryArgs) { - - for (NSString *dictionaryKey in [dictionaryArgs allKeys]) { - - // Prefix the key with a colon. - NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey]; - - if (_traceExecution) { - NSLog(@"%@ = %@", parameterName, [dictionaryArgs objectForKey:dictionaryKey]); - } - // Get the index for the parameter name. - int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]); - - FMDBRelease(parameterName); - - if (namedIdx > 0) { - // Standard binding from here. - [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt]; - - // increment the binding count, so our check below works out - idx++; - } - else { - NSString *message = [NSString stringWithFormat:@"Could not find index for %@", dictionaryKey]; - - if (_logsErrors) { - NSLog(@"%@", message); - } - if (outErr) { - *outErr = [self errorWithMessage:message]; - } - } - } - } - else { - - while (idx < queryCount) { - - if (arrayArgs && idx < (int)[arrayArgs count]) { - obj = [arrayArgs objectAtIndex:(NSUInteger)idx]; - } - else if (args) { - obj = va_arg(args, id); - } - else { - //We ran out of arguments - break; - } - - if (_traceExecution) { - if ([obj isKindOfClass:[NSData class]]) { - NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]); - } - else { - NSLog(@"obj: %@", obj); - } - } - - idx++; - - [self bindObject:obj toColumn:idx inStatement:pStmt]; - } - } - - - if (idx != queryCount) { - NSString *message = [NSString stringWithFormat:@"Error: the bind count (%d) is not correct for the # of variables in the query (%d) (%@) (executeUpdate)", idx, queryCount, sql]; - if (_logsErrors) { - NSLog(@"%@", message); - } - if (outErr) { - *outErr = [self errorWithMessage:message]; - } - - sqlite3_finalize(pStmt); - _isExecutingStatement = NO; - return NO; - } - - /* Call sqlite3_step() to run the virtual machine. Since the SQL being - ** executed is not a SELECT statement, we assume no data will be returned. - */ - - rc = sqlite3_step(pStmt); - - if (SQLITE_DONE == rc) { - // all is well, let's return. - } - else if (rc == SQLITE_ROW) { - NSString *message = [NSString stringWithFormat:@"A executeUpdate is being called with a query string '%@'", sql]; - if (_logsErrors) { - NSLog(@"%@", message); - NSLog(@"DB Query: %@", sql); - } - if (outErr) { - *outErr = [self errorWithMessage:message]; - } - } - else { - if (outErr) { - *outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]]; - } - - if (SQLITE_ERROR == rc) { - if (_logsErrors) { - NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db)); - NSLog(@"DB Query: %@", sql); - } - } - else if (SQLITE_MISUSE == rc) { - // uh oh. - if (_logsErrors) { - NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db)); - NSLog(@"DB Query: %@", sql); - } - } - else { - // wtf? - if (_logsErrors) { - NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db)); - NSLog(@"DB Query: %@", sql); - } - } - } - - if (_shouldCacheStatements && !cachedStmt) { - cachedStmt = [[FMStatement alloc] init]; - - [cachedStmt setStatement:pStmt]; - - [self setCachedStatement:cachedStmt forQuery:sql]; - - FMDBRelease(cachedStmt); - } - - int closeErrorCode; - - if (cachedStmt) { - [cachedStmt setUseCount:[cachedStmt useCount] + 1]; - closeErrorCode = sqlite3_reset(pStmt); - } - else { - /* Finalize the virtual machine. This releases all memory and other - ** resources allocated by the sqlite3_prepare() call above. - */ - closeErrorCode = sqlite3_finalize(pStmt); - } - - if (closeErrorCode != SQLITE_OK) { - if (_logsErrors) { - NSLog(@"Unknown error finalizing or resetting statement (%d: %s)", closeErrorCode, sqlite3_errmsg(_db)); - NSLog(@"DB Query: %@", sql); - } - } - - _isExecutingStatement = NO; - return (rc == SQLITE_DONE || rc == SQLITE_OK); -} - - -- (BOOL)executeUpdate:(NSString*)sql, ... { - va_list args; - va_start(args, sql); - - BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args]; - - va_end(args); - return result; -} - -- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments { - return [self executeUpdate:sql error:nil withArgumentsInArray:arguments orDictionary:nil orVAList:nil]; -} - -- (BOOL)executeUpdate:(NSString*)sql values:(NSArray *)values error:(NSError * __autoreleasing *)error { - return [self executeUpdate:sql error:error withArgumentsInArray:values orDictionary:nil orVAList:nil]; -} - -- (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments { - return [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:arguments orVAList:nil]; -} - -- (BOOL)executeUpdate:(NSString*)sql withVAList:(va_list)args { - return [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args]; -} - -- (BOOL)executeUpdateWithFormat:(NSString*)format, ... { - va_list args; - va_start(args, format); - - NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]]; - NSMutableArray *arguments = [NSMutableArray array]; - - [self extractSQL:format argumentsList:args intoString:sql arguments:arguments]; - - va_end(args); - - return [self executeUpdate:sql withArgumentsInArray:arguments]; -} - - -int FMDBExecuteBulkSQLCallback(void *theBlockAsVoid, int columns, char **values, char **names); // shhh clang. -int FMDBExecuteBulkSQLCallback(void *theBlockAsVoid, int columns, char **values, char **names) { - - if (!theBlockAsVoid) { - return SQLITE_OK; - } - - int (^execCallbackBlock)(NSDictionary *resultsDictionary) = (__bridge int (^)(NSDictionary *__strong))(theBlockAsVoid); - - NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:(NSUInteger)columns]; - - for (NSInteger i = 0; i < columns; i++) { - NSString *key = [NSString stringWithUTF8String:names[i]]; - id value = values[i] ? [NSString stringWithUTF8String:values[i]] : [NSNull null]; - [dictionary setObject:value forKey:key]; - } - - return execCallbackBlock(dictionary); -} - -- (BOOL)executeStatements:(NSString *)sql { - return [self executeStatements:sql withResultBlock:nil]; -} - -- (BOOL)executeStatements:(NSString *)sql withResultBlock:(FMDBExecuteStatementsCallbackBlock)block { - - int rc; - char *errmsg = nil; - - rc = sqlite3_exec([self sqliteHandle], [sql UTF8String], block ? FMDBExecuteBulkSQLCallback : nil, (__bridge void *)(block), &errmsg); - - if (errmsg && [self logsErrors]) { - NSLog(@"Error inserting batch: %s", errmsg); - sqlite3_free(errmsg); - } - - return (rc == SQLITE_OK); -} - -- (BOOL)executeUpdate:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... { - - va_list args; - va_start(args, outErr); - - BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:args]; - - va_end(args); - return result; -} - - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" -- (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... { - va_list args; - va_start(args, outErr); - - BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:args]; - - va_end(args); - return result; -} - -#pragma clang diagnostic pop - -#pragma mark Transactions - -- (BOOL)rollback { - BOOL b = [self executeUpdate:@"rollback transaction"]; - - if (b) { - _inTransaction = NO; - } - - return b; -} - -- (BOOL)commit { - BOOL b = [self executeUpdate:@"commit transaction"]; - - if (b) { - _inTransaction = NO; - } - - return b; -} - -- (BOOL)beginDeferredTransaction { - - BOOL b = [self executeUpdate:@"begin deferred transaction"]; - if (b) { - _inTransaction = YES; - } - - return b; -} - -- (BOOL)beginTransaction { - - BOOL b = [self executeUpdate:@"begin exclusive transaction"]; - if (b) { - _inTransaction = YES; - } - - return b; -} - -- (BOOL)inTransaction { - return _inTransaction; -} - -static NSString *FMDBEscapeSavePointName(NSString *savepointName) { - return [savepointName stringByReplacingOccurrencesOfString:@"'" withString:@"''"]; -} - -- (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr { -#if SQLITE_VERSION_NUMBER >= 3007000 - NSParameterAssert(name); - - NSString *sql = [NSString stringWithFormat:@"savepoint '%@';", FMDBEscapeSavePointName(name)]; - - return [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:nil]; -#else - NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); - if (self.logsErrors) NSLog(@"%@", errorMessage); - return NO; -#endif -} - -- (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr { -#if SQLITE_VERSION_NUMBER >= 3007000 - NSParameterAssert(name); - - NSString *sql = [NSString stringWithFormat:@"release savepoint '%@';", FMDBEscapeSavePointName(name)]; - - return [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:nil]; -#else - NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); - if (self.logsErrors) NSLog(@"%@", errorMessage); - return NO; -#endif -} - -- (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr { -#if SQLITE_VERSION_NUMBER >= 3007000 - NSParameterAssert(name); - - NSString *sql = [NSString stringWithFormat:@"rollback transaction to savepoint '%@';", FMDBEscapeSavePointName(name)]; - - return [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:nil]; -#else - NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); - if (self.logsErrors) NSLog(@"%@", errorMessage); - return NO; -#endif -} - -- (NSError*)inSavePoint:(void (^)(BOOL *rollback))block { -#if SQLITE_VERSION_NUMBER >= 3007000 - static unsigned long savePointIdx = 0; - - NSString *name = [NSString stringWithFormat:@"dbSavePoint%ld", savePointIdx++]; - - BOOL shouldRollback = NO; - - NSError *err = 0x00; - - if (![self startSavePointWithName:name error:&err]) { - return err; - } - - if (block) { - block(&shouldRollback); - } - - if (shouldRollback) { - // We need to rollback and release this savepoint to remove it - [self rollbackToSavePointWithName:name error:&err]; - } - [self releaseSavePointWithName:name error:&err]; - - return err; -#else - NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); - if (self.logsErrors) NSLog(@"%@", errorMessage); - return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; -#endif -} - - -#pragma mark Cache statements - -- (BOOL)shouldCacheStatements { - return _shouldCacheStatements; -} - -- (void)setShouldCacheStatements:(BOOL)value { - - _shouldCacheStatements = value; - - if (_shouldCacheStatements && !_cachedStatements) { - [self setCachedStatements:[NSMutableDictionary dictionary]]; - } - - if (!_shouldCacheStatements) { - [self setCachedStatements:nil]; - } -} - -#pragma mark Callback function - -void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv); // -Wmissing-prototypes -void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv) { -#if ! __has_feature(objc_arc) - void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (id)sqlite3_user_data(context); -#else - void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (__bridge id)sqlite3_user_data(context); -#endif - if (block) { - block(context, argc, argv); - } -} - - -- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(void *context, int argc, void **argv))block { - - if (!_openFunctions) { - _openFunctions = [NSMutableSet new]; - } - - id b = FMDBReturnAutoreleased([block copy]); - - [_openFunctions addObject:b]; - - /* I tried adding custom functions to release the block when the connection is destroyed- but they seemed to never be called, so we use _openFunctions to store the values instead. */ -#if ! __has_feature(objc_arc) - sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00); -#else - sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (__bridge void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00); -#endif -} - -@end - - - -@implementation FMStatement -@synthesize statement=_statement; -@synthesize query=_query; -@synthesize useCount=_useCount; -@synthesize inUse=_inUse; - -- (void)finalize { - [self close]; - [super finalize]; -} - -- (void)dealloc { - [self close]; - FMDBRelease(_query); -#if ! __has_feature(objc_arc) - [super dealloc]; -#endif -} - -- (void)close { - if (_statement) { - sqlite3_finalize(_statement); - _statement = 0x00; - } - - _inUse = NO; -} - -- (void)reset { - if (_statement) { - sqlite3_reset(_statement); - } - - _inUse = NO; -} - -- (NSString*)description { - return [NSString stringWithFormat:@"%@ %ld hit(s) for query %@", [super description], _useCount, _query]; -} - - -@end - diff --git a/Pods/FMDB/src/fmdb/FMDatabaseAdditions.h b/Pods/FMDB/src/fmdb/FMDatabaseAdditions.h deleted file mode 100644 index 9dd0b62..0000000 --- a/Pods/FMDB/src/fmdb/FMDatabaseAdditions.h +++ /dev/null @@ -1,278 +0,0 @@ -// -// FMDatabaseAdditions.h -// fmdb -// -// Created by August Mueller on 10/30/05. -// Copyright 2005 Flying Meat Inc.. All rights reserved. -// - -#import -#import "FMDatabase.h" - - -/** Category of additions for `` class. - - ### See also - - - `` - */ - -@interface FMDatabase (FMDatabaseAdditions) - -///---------------------------------------- -/// @name Return results of SQL to variable -///---------------------------------------- - -/** Return `int` value for query - - @param query The SQL query to be performed. - @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. - - @return `int` value. - - @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. - */ - -- (int)intForQuery:(NSString*)query, ...; - -/** Return `long` value for query - - @param query The SQL query to be performed. - @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. - - @return `long` value. - - @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. - */ - -- (long)longForQuery:(NSString*)query, ...; - -/** Return `BOOL` value for query - - @param query The SQL query to be performed. - @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. - - @return `BOOL` value. - - @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. - */ - -- (BOOL)boolForQuery:(NSString*)query, ...; - -/** Return `double` value for query - - @param query The SQL query to be performed. - @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. - - @return `double` value. - - @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. - */ - -- (double)doubleForQuery:(NSString*)query, ...; - -/** Return `NSString` value for query - - @param query The SQL query to be performed. - @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. - - @return `NSString` value. - - @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. - */ - -- (NSString*)stringForQuery:(NSString*)query, ...; - -/** Return `NSData` value for query - - @param query The SQL query to be performed. - @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. - - @return `NSData` value. - - @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. - */ - -- (NSData*)dataForQuery:(NSString*)query, ...; - -/** Return `NSDate` value for query - - @param query The SQL query to be performed. - @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. - - @return `NSDate` value. - - @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. - */ - -- (NSDate*)dateForQuery:(NSString*)query, ...; - - -// Notice that there's no dataNoCopyForQuery:. -// That would be a bad idea, because we close out the result set, and then what -// happens to the data that we just didn't copy? Who knows, not I. - - -///-------------------------------- -/// @name Schema related operations -///-------------------------------- - -/** Does table exist in database? - - @param tableName The name of the table being looked for. - - @return `YES` if table found; `NO` if not found. - */ - -- (BOOL)tableExists:(NSString*)tableName; - -/** The schema of the database. - - This will be the schema for the entire database. For each entity, each row of the result set will include the following fields: - - - `type` - The type of entity (e.g. table, index, view, or trigger) - - `name` - The name of the object - - `tbl_name` - The name of the table to which the object references - - `rootpage` - The page number of the root b-tree page for tables and indices - - `sql` - The SQL that created the entity - - @return `FMResultSet` of schema; `nil` on error. - - @see [SQLite File Format](http://www.sqlite.org/fileformat.html) - */ - -- (FMResultSet*)getSchema; - -/** The schema of the database. - - This will be the schema for a particular table as report by SQLite `PRAGMA`, for example: - - PRAGMA table_info('employees') - - This will report: - - - `cid` - The column ID number - - `name` - The name of the column - - `type` - The data type specified for the column - - `notnull` - whether the field is defined as NOT NULL (i.e. values required) - - `dflt_value` - The default value for the column - - `pk` - Whether the field is part of the primary key of the table - - @param tableName The name of the table for whom the schema will be returned. - - @return `FMResultSet` of schema; `nil` on error. - - @see [table_info](http://www.sqlite.org/pragma.html#pragma_table_info) - */ - -- (FMResultSet*)getTableSchema:(NSString*)tableName; - -/** Test to see if particular column exists for particular table in database - - @param columnName The name of the column. - - @param tableName The name of the table. - - @return `YES` if column exists in table in question; `NO` otherwise. - */ - -- (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName; - -/** Test to see if particular column exists for particular table in database - - @param columnName The name of the column. - - @param tableName The name of the table. - - @return `YES` if column exists in table in question; `NO` otherwise. - - @see columnExists:inTableWithName: - - @warning Deprecated - use `` instead. - */ - -- (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)); - - -/** Validate SQL statement - - This validates SQL statement by performing `sqlite3_prepare_v2`, but not returning the results, but instead immediately calling `sqlite3_finalize`. - - @param sql The SQL statement being validated. - - @param error This is a pointer to a `NSError` object that will receive the autoreleased `NSError` object if there was any error. If this is `nil`, no `NSError` result will be returned. - - @return `YES` if validation succeeded without incident; `NO` otherwise. - - */ - -- (BOOL)validateSQL:(NSString*)sql error:(NSError**)error; - - -///----------------------------------- -/// @name Application identifier tasks -///----------------------------------- - -/** Retrieve application ID - - @return The `uint32_t` numeric value of the application ID. - - @see setApplicationID: - */ - -- (uint32_t)applicationID; - -/** Set the application ID - - @param appID The `uint32_t` numeric value of the application ID. - - @see applicationID - */ - -- (void)setApplicationID:(uint32_t)appID; - -#if TARGET_OS_MAC && !TARGET_OS_IPHONE -/** Retrieve application ID string - - @return The `NSString` value of the application ID. - - @see setApplicationIDString: - */ - - -- (NSString*)applicationIDString; - -/** Set the application ID string - - @param string The `NSString` value of the application ID. - - @see applicationIDString - */ - -- (void)setApplicationIDString:(NSString*)string; - -#endif - -///----------------------------------- -/// @name user version identifier tasks -///----------------------------------- - -/** Retrieve user version - - @return The `uint32_t` numeric value of the user version. - - @see setUserVersion: - */ - -- (uint32_t)userVersion; - -/** Set the user-version - - @param version The `uint32_t` numeric value of the user version. - - @see userVersion - */ - -- (void)setUserVersion:(uint32_t)version; - -@end diff --git a/Pods/FMDB/src/fmdb/FMDatabaseAdditions.m b/Pods/FMDB/src/fmdb/FMDatabaseAdditions.m deleted file mode 100644 index 61fa747..0000000 --- a/Pods/FMDB/src/fmdb/FMDatabaseAdditions.m +++ /dev/null @@ -1,246 +0,0 @@ -// -// FMDatabaseAdditions.m -// fmdb -// -// Created by August Mueller on 10/30/05. -// Copyright 2005 Flying Meat Inc.. All rights reserved. -// - -#import "FMDatabase.h" -#import "FMDatabaseAdditions.h" -#import "TargetConditionals.h" - -#if FMDB_SQLITE_STANDALONE -#import -#else -#import -#endif - -@interface FMDatabase (PrivateStuff) -- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; -@end - -@implementation FMDatabase (FMDatabaseAdditions) - -#define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \ -va_list args; \ -va_start(args, query); \ -FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orDictionary:0x00 orVAList:args]; \ -va_end(args); \ -if (![resultSet next]) { return (type)0; } \ -type ret = [resultSet sel:0]; \ -[resultSet close]; \ -[resultSet setParentDB:nil]; \ -return ret; - - -- (NSString*)stringForQuery:(NSString*)query, ... { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex); -} - -- (int)intForQuery:(NSString*)query, ... { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex); -} - -- (long)longForQuery:(NSString*)query, ... { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex); -} - -- (BOOL)boolForQuery:(NSString*)query, ... { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex); -} - -- (double)doubleForQuery:(NSString*)query, ... { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex); -} - -- (NSData*)dataForQuery:(NSString*)query, ... { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex); -} - -- (NSDate*)dateForQuery:(NSString*)query, ... { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex); -} - - -- (BOOL)tableExists:(NSString*)tableName { - - tableName = [tableName lowercaseString]; - - FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName]; - - //if at least one next exists, table exists - BOOL returnBool = [rs next]; - - //close and free object - [rs close]; - - return returnBool; -} - -/* - get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] - check if table exist in database (patch from OZLB) -*/ -- (FMResultSet*)getSchema { - - //result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] - FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"]; - - return rs; -} - -/* - get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] -*/ -- (FMResultSet*)getTableSchema:(NSString*)tableName { - - //result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] - FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"pragma table_info('%@')", tableName]]; - - return rs; -} - -- (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName { - - BOOL returnBool = NO; - - tableName = [tableName lowercaseString]; - columnName = [columnName lowercaseString]; - - FMResultSet *rs = [self getTableSchema:tableName]; - - //check if column is present in table schema - while ([rs next]) { - if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString:columnName]) { - returnBool = YES; - break; - } - } - - //If this is not done FMDatabase instance stays out of pool - [rs close]; - - return returnBool; -} - - - -- (uint32_t)applicationID { -#if SQLITE_VERSION_NUMBER >= 3007017 - uint32_t r = 0; - - FMResultSet *rs = [self executeQuery:@"pragma application_id"]; - - if ([rs next]) { - r = (uint32_t)[rs longLongIntForColumnIndex:0]; - } - - [rs close]; - - return r; -#else - NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); - if (self.logsErrors) NSLog(@"%@", errorMessage); - return 0; -#endif -} - -- (void)setApplicationID:(uint32_t)appID { -#if SQLITE_VERSION_NUMBER >= 3007017 - NSString *query = [NSString stringWithFormat:@"pragma application_id=%d", appID]; - FMResultSet *rs = [self executeQuery:query]; - [rs next]; - [rs close]; -#else - NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); - if (self.logsErrors) NSLog(@"%@", errorMessage); -#endif -} - - -#if TARGET_OS_MAC && !TARGET_OS_IPHONE - -- (NSString*)applicationIDString { -#if SQLITE_VERSION_NUMBER >= 3007017 - NSString *s = NSFileTypeForHFSTypeCode([self applicationID]); - - assert([s length] == 6); - - s = [s substringWithRange:NSMakeRange(1, 4)]; - - - return s; -#else - NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); - if (self.logsErrors) NSLog(@"%@", errorMessage); - return nil; -#endif -} - -- (void)setApplicationIDString:(NSString*)s { -#if SQLITE_VERSION_NUMBER >= 3007017 - if ([s length] != 4) { - NSLog(@"setApplicationIDString: string passed is not exactly 4 chars long. (was %ld)", [s length]); - } - - [self setApplicationID:NSHFSTypeCodeFromFileType([NSString stringWithFormat:@"'%@'", s])]; -#else - NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); - if (self.logsErrors) NSLog(@"%@", errorMessage); -#endif -} - -#endif - -- (uint32_t)userVersion { - uint32_t r = 0; - - FMResultSet *rs = [self executeQuery:@"pragma user_version"]; - - if ([rs next]) { - r = (uint32_t)[rs longLongIntForColumnIndex:0]; - } - - [rs close]; - return r; -} - -- (void)setUserVersion:(uint32_t)version { - NSString *query = [NSString stringWithFormat:@"pragma user_version = %d", version]; - FMResultSet *rs = [self executeQuery:query]; - [rs next]; - [rs close]; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" - -- (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)) { - return [self columnExists:columnName inTableWithName:tableName]; -} - -#pragma clang diagnostic pop - - -- (BOOL)validateSQL:(NSString*)sql error:(NSError**)error { - sqlite3_stmt *pStmt = NULL; - BOOL validationSucceeded = YES; - - int rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); - if (rc != SQLITE_OK) { - validationSucceeded = NO; - if (error) { - *error = [NSError errorWithDomain:NSCocoaErrorDomain - code:[self lastErrorCode] - userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage] - forKey:NSLocalizedDescriptionKey]]; - } - } - - sqlite3_finalize(pStmt); - - return validationSucceeded; -} - -@end diff --git a/Pods/FMDB/src/fmdb/FMDatabasePool.h b/Pods/FMDB/src/fmdb/FMDatabasePool.h deleted file mode 100644 index 1915858..0000000 --- a/Pods/FMDB/src/fmdb/FMDatabasePool.h +++ /dev/null @@ -1,200 +0,0 @@ -// -// FMDatabasePool.h -// fmdb -// -// Created by August Mueller on 6/22/11. -// Copyright 2011 Flying Meat Inc. All rights reserved. -// - -#import - -@class FMDatabase; - -/** Pool of `` objects. - - ### See also - - - `` - - `` - - @warning Before using `FMDatabasePool`, please consider using `` instead. - - If you really really really know what you're doing and `FMDatabasePool` is what - you really really need (ie, you're using a read only database), OK you can use - it. But just be careful not to deadlock! - - For an example on deadlocking, search for: - `ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD` - in the main.m file. - */ - -@interface FMDatabasePool : NSObject { - NSString *_path; - - dispatch_queue_t _lockQueue; - - NSMutableArray *_databaseInPool; - NSMutableArray *_databaseOutPool; - - __unsafe_unretained id _delegate; - - NSUInteger _maximumNumberOfDatabasesToCreate; - int _openFlags; -} - -/** Database path */ - -@property (atomic, retain) NSString *path; - -/** Delegate object */ - -@property (atomic, assign) id delegate; - -/** Maximum number of databases to create */ - -@property (atomic, assign) NSUInteger maximumNumberOfDatabasesToCreate; - -/** Open flags */ - -@property (atomic, readonly) int openFlags; - - -///--------------------- -/// @name Initialization -///--------------------- - -/** Create pool using path. - - @param aPath The file path of the database. - - @return The `FMDatabasePool` object. `nil` on error. - */ - -+ (instancetype)databasePoolWithPath:(NSString*)aPath; - -/** Create pool using path and specified flags - - @param aPath The file path of the database. - @param openFlags Flags passed to the openWithFlags method of the database - - @return The `FMDatabasePool` object. `nil` on error. - */ - -+ (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags; - -/** Create pool using path. - - @param aPath The file path of the database. - - @return The `FMDatabasePool` object. `nil` on error. - */ - -- (instancetype)initWithPath:(NSString*)aPath; - -/** Create pool using path and specified flags. - - @param aPath The file path of the database. - @param openFlags Flags passed to the openWithFlags method of the database - - @return The `FMDatabasePool` object. `nil` on error. - */ - -- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags; - -///------------------------------------------------ -/// @name Keeping track of checked in/out databases -///------------------------------------------------ - -/** Number of checked-in databases in pool - - @returns Number of databases - */ - -- (NSUInteger)countOfCheckedInDatabases; - -/** Number of checked-out databases in pool - - @returns Number of databases - */ - -- (NSUInteger)countOfCheckedOutDatabases; - -/** Total number of databases in pool - - @returns Number of databases - */ - -- (NSUInteger)countOfOpenDatabases; - -/** Release all databases in pool */ - -- (void)releaseAllDatabases; - -///------------------------------------------ -/// @name Perform database operations in pool -///------------------------------------------ - -/** Synchronously perform database operations in pool. - - @param block The code to be run on the `FMDatabasePool` pool. - */ - -- (void)inDatabase:(void (^)(FMDatabase *db))block; - -/** Synchronously perform database operations in pool using transaction. - - @param block The code to be run on the `FMDatabasePool` pool. - */ - -- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; - -/** Synchronously perform database operations in pool using deferred transaction. - - @param block The code to be run on the `FMDatabasePool` pool. - */ - -- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; - -/** Synchronously perform database operations in pool using save point. - - @param block The code to be run on the `FMDatabasePool` pool. - - @return `NSError` object if error; `nil` if successful. - - @warning You can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. If you need to nest, use `<[FMDatabase startSavePointWithName:error:]>` instead. -*/ - -- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; - -@end - - -/** FMDatabasePool delegate category - - This is a category that defines the protocol for the FMDatabasePool delegate - */ - -@interface NSObject (FMDatabasePoolDelegate) - -/** Asks the delegate whether database should be added to the pool. - - @param pool The `FMDatabasePool` object. - @param database The `FMDatabase` object. - - @return `YES` if it should add database to pool; `NO` if not. - - */ - -- (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database; - -/** Tells the delegate that database was added to the pool. - - @param pool The `FMDatabasePool` object. - @param database The `FMDatabase` object. - - */ - -- (void)databasePool:(FMDatabasePool*)pool didAddDatabase:(FMDatabase*)database; - -@end - diff --git a/Pods/FMDB/src/fmdb/FMDatabasePool.m b/Pods/FMDB/src/fmdb/FMDatabasePool.m deleted file mode 100644 index e8e52cb..0000000 --- a/Pods/FMDB/src/fmdb/FMDatabasePool.m +++ /dev/null @@ -1,283 +0,0 @@ -// -// FMDatabasePool.m -// fmdb -// -// Created by August Mueller on 6/22/11. -// Copyright 2011 Flying Meat Inc. All rights reserved. -// - -#if FMDB_SQLITE_STANDALONE -#import -#else -#import -#endif - -#import "FMDatabasePool.h" -#import "FMDatabase.h" - -@interface FMDatabasePool() - -- (void)pushDatabaseBackInPool:(FMDatabase*)db; -- (FMDatabase*)db; - -@end - - -@implementation FMDatabasePool -@synthesize path=_path; -@synthesize delegate=_delegate; -@synthesize maximumNumberOfDatabasesToCreate=_maximumNumberOfDatabasesToCreate; -@synthesize openFlags=_openFlags; - - -+ (instancetype)databasePoolWithPath:(NSString*)aPath { - return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]); -} - -+ (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags { - return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath flags:openFlags]); -} - -- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { - - self = [super init]; - - if (self != nil) { - _path = [aPath copy]; - _lockQueue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); - _databaseInPool = FMDBReturnRetained([NSMutableArray array]); - _databaseOutPool = FMDBReturnRetained([NSMutableArray array]); - _openFlags = openFlags; - } - - return self; -} - -- (instancetype)initWithPath:(NSString*)aPath -{ - // default flags for sqlite3_open - return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE]; -} - -- (instancetype)init { - return [self initWithPath:nil]; -} - - -- (void)dealloc { - - _delegate = 0x00; - FMDBRelease(_path); - FMDBRelease(_databaseInPool); - FMDBRelease(_databaseOutPool); - - if (_lockQueue) { - FMDBDispatchQueueRelease(_lockQueue); - _lockQueue = 0x00; - } -#if ! __has_feature(objc_arc) - [super dealloc]; -#endif -} - - -- (void)executeLocked:(void (^)(void))aBlock { - dispatch_sync(_lockQueue, aBlock); -} - -- (void)pushDatabaseBackInPool:(FMDatabase*)db { - - if (!db) { // db can be null if we set an upper bound on the # of databases to create. - return; - } - - [self executeLocked:^() { - - if ([self->_databaseInPool containsObject:db]) { - [[NSException exceptionWithName:@"Database already in pool" reason:@"The FMDatabase being put back into the pool is already present in the pool" userInfo:nil] raise]; - } - - [self->_databaseInPool addObject:db]; - [self->_databaseOutPool removeObject:db]; - - }]; -} - -- (FMDatabase*)db { - - __block FMDatabase *db; - - - [self executeLocked:^() { - db = [self->_databaseInPool lastObject]; - - BOOL shouldNotifyDelegate = NO; - - if (db) { - [self->_databaseOutPool addObject:db]; - [self->_databaseInPool removeLastObject]; - } - else { - - if (self->_maximumNumberOfDatabasesToCreate) { - NSUInteger currentCount = [self->_databaseOutPool count] + [self->_databaseInPool count]; - - if (currentCount >= self->_maximumNumberOfDatabasesToCreate) { - NSLog(@"Maximum number of databases (%ld) has already been reached!", (long)currentCount); - return; - } - } - - db = [FMDatabase databaseWithPath:self->_path]; - shouldNotifyDelegate = YES; - } - - //This ensures that the db is opened before returning -#if SQLITE_VERSION_NUMBER >= 3005000 - BOOL success = [db openWithFlags:self->_openFlags]; -#else - BOOL success = [db open]; -#endif - if (success) { - if ([self->_delegate respondsToSelector:@selector(databasePool:shouldAddDatabaseToPool:)] && ![self->_delegate databasePool:self shouldAddDatabaseToPool:db]) { - [db close]; - db = 0x00; - } - else { - //It should not get added in the pool twice if lastObject was found - if (![self->_databaseOutPool containsObject:db]) { - [self->_databaseOutPool addObject:db]; - - if (shouldNotifyDelegate && [self->_delegate respondsToSelector:@selector(databasePool:didAddDatabase:)]) { - [self->_delegate databasePool:self didAddDatabase:db]; - } - } - } - } - else { - NSLog(@"Could not open up the database at path %@", self->_path); - db = 0x00; - } - }]; - - return db; -} - -- (NSUInteger)countOfCheckedInDatabases { - - __block NSUInteger count; - - [self executeLocked:^() { - count = [self->_databaseInPool count]; - }]; - - return count; -} - -- (NSUInteger)countOfCheckedOutDatabases { - - __block NSUInteger count; - - [self executeLocked:^() { - count = [self->_databaseOutPool count]; - }]; - - return count; -} - -- (NSUInteger)countOfOpenDatabases { - __block NSUInteger count; - - [self executeLocked:^() { - count = [self->_databaseOutPool count] + [self->_databaseInPool count]; - }]; - - return count; -} - -- (void)releaseAllDatabases { - [self executeLocked:^() { - [self->_databaseOutPool removeAllObjects]; - [self->_databaseInPool removeAllObjects]; - }]; -} - -- (void)inDatabase:(void (^)(FMDatabase *db))block { - - FMDatabase *db = [self db]; - - block(db); - - [self pushDatabaseBackInPool:db]; -} - -- (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { - - BOOL shouldRollback = NO; - - FMDatabase *db = [self db]; - - if (useDeferred) { - [db beginDeferredTransaction]; - } - else { - [db beginTransaction]; - } - - - block(db, &shouldRollback); - - if (shouldRollback) { - [db rollback]; - } - else { - [db commit]; - } - - [self pushDatabaseBackInPool:db]; -} - -- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { - [self beginTransaction:YES withBlock:block]; -} - -- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { - [self beginTransaction:NO withBlock:block]; -} - -- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { -#if SQLITE_VERSION_NUMBER >= 3007000 - static unsigned long savePointIdx = 0; - - NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; - - BOOL shouldRollback = NO; - - FMDatabase *db = [self db]; - - NSError *err = 0x00; - - if (![db startSavePointWithName:name error:&err]) { - [self pushDatabaseBackInPool:db]; - return err; - } - - block(db, &shouldRollback); - - if (shouldRollback) { - // We need to rollback and release this savepoint to remove it - [db rollbackToSavePointWithName:name error:&err]; - } - [db releaseSavePointWithName:name error:&err]; - - [self pushDatabaseBackInPool:db]; - - return err; -#else - NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); - if (self.logsErrors) NSLog(@"%@", errorMessage); - return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; -#endif -} - -@end diff --git a/Pods/FMDB/src/fmdb/FMDatabaseQueue.h b/Pods/FMDB/src/fmdb/FMDatabaseQueue.h deleted file mode 100644 index ae45b65..0000000 --- a/Pods/FMDB/src/fmdb/FMDatabaseQueue.h +++ /dev/null @@ -1,182 +0,0 @@ -// -// FMDatabaseQueue.h -// fmdb -// -// Created by August Mueller on 6/22/11. -// Copyright 2011 Flying Meat Inc. All rights reserved. -// - -#import - -@class FMDatabase; - -/** To perform queries and updates on multiple threads, you'll want to use `FMDatabaseQueue`. - - Using a single instance of `` from multiple threads at once is a bad idea. It has always been OK to make a `` object *per thread*. Just don't share a single instance across threads, and definitely not across multiple threads at the same time. - - Instead, use `FMDatabaseQueue`. Here's how to use it: - - First, make your queue. - - FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; - - Then use it like so: - - [queue inDatabase:^(FMDatabase *db) { - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; - - FMResultSet *rs = [db executeQuery:@"select * from foo"]; - while ([rs next]) { - //… - } - }]; - - An easy way to wrap things up in a transaction can be done like this: - - [queue inTransaction:^(FMDatabase *db, BOOL *rollback) { - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; - - if (whoopsSomethingWrongHappened) { - *rollback = YES; - return; - } - // etc… - [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]]; - }]; - - `FMDatabaseQueue` will run the blocks on a serialized queue (hence the name of the class). So if you call `FMDatabaseQueue`'s methods from multiple threads at the same time, they will be executed in the order they are received. This way queries and updates won't step on each other's toes, and every one is happy. - - ### See also - - - `` - - @warning Do not instantiate a single `` object and use it across multiple threads. Use `FMDatabaseQueue` instead. - - @warning The calls to `FMDatabaseQueue`'s methods are blocking. So even though you are passing along blocks, they will **not** be run on another thread. - - */ - -@interface FMDatabaseQueue : NSObject { - NSString *_path; - dispatch_queue_t _queue; - FMDatabase *_db; - int _openFlags; -} - -/** Path of database */ - -@property (atomic, retain) NSString *path; - -/** Open flags */ - -@property (atomic, readonly) int openFlags; - -///---------------------------------------------------- -/// @name Initialization, opening, and closing of queue -///---------------------------------------------------- - -/** Create queue using path. - - @param aPath The file path of the database. - - @return The `FMDatabaseQueue` object. `nil` on error. - */ - -+ (instancetype)databaseQueueWithPath:(NSString*)aPath; - -/** Create queue using path and specified flags. - - @param aPath The file path of the database. - @param openFlags Flags passed to the openWithFlags method of the database - - @return The `FMDatabaseQueue` object. `nil` on error. - */ -+ (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags; - -/** Create queue using path. - - @param aPath The file path of the database. - - @return The `FMDatabaseQueue` object. `nil` on error. - */ - -- (instancetype)initWithPath:(NSString*)aPath; - -/** Create queue using path and specified flags. - - @param aPath The file path of the database. - @param openFlags Flags passed to the openWithFlags method of the database - - @return The `FMDatabaseQueue` object. `nil` on error. - */ - -- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags; - -/** Create queue using path and specified flags. - - @param aPath The file path of the database. - @param openFlags Flags passed to the openWithFlags method of the database - @param vfsName The name of a custom virtual file system - - @return The `FMDatabaseQueue` object. `nil` on error. - */ - -- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName; - -/** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object. - - Subclasses can override this method to return specified Class of 'FMDatabase' subclass. - - @return The Class of 'FMDatabase' subclass, that will be used to instantiate database object. - */ - -+ (Class)databaseClass; - -/** Close database used by queue. */ - -- (void)close; - -///----------------------------------------------- -/// @name Dispatching database operations to queue -///----------------------------------------------- - -/** Synchronously perform database operations on queue. - - @param block The code to be run on the queue of `FMDatabaseQueue` - */ - -- (void)inDatabase:(void (^)(FMDatabase *db))block; - -/** Synchronously perform database operations on queue, using transactions. - - @param block The code to be run on the queue of `FMDatabaseQueue` - */ - -- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; - -/** Synchronously perform database operations on queue, using deferred transactions. - - @param block The code to be run on the queue of `FMDatabaseQueue` - */ - -- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; - -///----------------------------------------------- -/// @name Dispatching database operations to queue -///----------------------------------------------- - -/** Synchronously perform database operations using save point. - - @param block The code to be run on the queue of `FMDatabaseQueue` - */ - -// NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. -// If you need to nest, use FMDatabase's startSavePointWithName:error: instead. -- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; - -@end - diff --git a/Pods/FMDB/src/fmdb/FMDatabaseQueue.m b/Pods/FMDB/src/fmdb/FMDatabaseQueue.m deleted file mode 100644 index c877a34..0000000 --- a/Pods/FMDB/src/fmdb/FMDatabaseQueue.m +++ /dev/null @@ -1,245 +0,0 @@ -// -// FMDatabaseQueue.m -// fmdb -// -// Created by August Mueller on 6/22/11. -// Copyright 2011 Flying Meat Inc. All rights reserved. -// - -#import "FMDatabaseQueue.h" -#import "FMDatabase.h" - -#if FMDB_SQLITE_STANDALONE -#import -#else -#import -#endif - -/* - - Note: we call [self retain]; before using dispatch_sync, just incase - FMDatabaseQueue is released on another thread and we're in the middle of doing - something in dispatch_sync - - */ - -/* - * A key used to associate the FMDatabaseQueue object with the dispatch_queue_t it uses. - * This in turn is used for deadlock detection by seeing if inDatabase: is called on - * the queue's dispatch queue, which should not happen and causes a deadlock. - */ -static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey; - -@implementation FMDatabaseQueue - -@synthesize path = _path; -@synthesize openFlags = _openFlags; - -+ (instancetype)databaseQueueWithPath:(NSString*)aPath { - - FMDatabaseQueue *q = [[self alloc] initWithPath:aPath]; - - FMDBAutorelease(q); - - return q; -} - -+ (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags { - - FMDatabaseQueue *q = [[self alloc] initWithPath:aPath flags:openFlags]; - - FMDBAutorelease(q); - - return q; -} - -+ (Class)databaseClass { - return [FMDatabase class]; -} - -- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName { - - self = [super init]; - - if (self != nil) { - - _db = [[[self class] databaseClass] databaseWithPath:aPath]; - FMDBRetain(_db); - -#if SQLITE_VERSION_NUMBER >= 3005000 - BOOL success = [_db openWithFlags:openFlags vfs:vfsName]; -#else - BOOL success = [_db open]; -#endif - if (!success) { - NSLog(@"Could not create database queue for path %@", aPath); - FMDBRelease(self); - return 0x00; - } - - _path = FMDBReturnRetained(aPath); - - _queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); - dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL); - _openFlags = openFlags; - } - - return self; -} - -- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { - return [self initWithPath:aPath flags:openFlags vfs:nil]; -} - -- (instancetype)initWithPath:(NSString*)aPath { - - // default flags for sqlite3_open - return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:nil]; -} - -- (instancetype)init { - return [self initWithPath:nil]; -} - - -- (void)dealloc { - - FMDBRelease(_db); - FMDBRelease(_path); - - if (_queue) { - FMDBDispatchQueueRelease(_queue); - _queue = 0x00; - } -#if ! __has_feature(objc_arc) - [super dealloc]; -#endif -} - -- (void)close { - FMDBRetain(self); - dispatch_sync(_queue, ^() { - [self->_db close]; - FMDBRelease(_db); - self->_db = 0x00; - }); - FMDBRelease(self); -} - -- (FMDatabase*)database { - if (!_db) { - _db = FMDBReturnRetained([FMDatabase databaseWithPath:_path]); - -#if SQLITE_VERSION_NUMBER >= 3005000 - BOOL success = [_db openWithFlags:_openFlags]; -#else - BOOL success = [_db open]; -#endif - if (!success) { - NSLog(@"FMDatabaseQueue could not reopen database for path %@", _path); - FMDBRelease(_db); - _db = 0x00; - return 0x00; - } - } - - return _db; -} - -- (void)inDatabase:(void (^)(FMDatabase *db))block { - /* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue - * and then check it against self to make sure we're not about to deadlock. */ - FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey); - assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock"); - - FMDBRetain(self); - - dispatch_sync(_queue, ^() { - - FMDatabase *db = [self database]; - block(db); - - if ([db hasOpenResultSets]) { - NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]"); - -#if defined(DEBUG) && DEBUG - NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]); - for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) { - FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue]; - NSLog(@"query: '%@'", [rs query]); - } -#endif - } - }); - - FMDBRelease(self); -} - - -- (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { - FMDBRetain(self); - dispatch_sync(_queue, ^() { - - BOOL shouldRollback = NO; - - if (useDeferred) { - [[self database] beginDeferredTransaction]; - } - else { - [[self database] beginTransaction]; - } - - block([self database], &shouldRollback); - - if (shouldRollback) { - [[self database] rollback]; - } - else { - [[self database] commit]; - } - }); - - FMDBRelease(self); -} - -- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { - [self beginTransaction:YES withBlock:block]; -} - -- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { - [self beginTransaction:NO withBlock:block]; -} - -- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { -#if SQLITE_VERSION_NUMBER >= 3007000 - static unsigned long savePointIdx = 0; - __block NSError *err = 0x00; - FMDBRetain(self); - dispatch_sync(_queue, ^() { - - NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; - - BOOL shouldRollback = NO; - - if ([[self database] startSavePointWithName:name error:&err]) { - - block([self database], &shouldRollback); - - if (shouldRollback) { - // We need to rollback and release this savepoint to remove it - [[self database] rollbackToSavePointWithName:name error:&err]; - } - [[self database] releaseSavePointWithName:name error:&err]; - - } - }); - FMDBRelease(self); - return err; -#else - NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); - if (self.logsErrors) NSLog(@"%@", errorMessage); - return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; -#endif -} - -@end diff --git a/Pods/FMDB/src/fmdb/FMResultSet.h b/Pods/FMDB/src/fmdb/FMResultSet.h deleted file mode 100644 index af0433b..0000000 --- a/Pods/FMDB/src/fmdb/FMResultSet.h +++ /dev/null @@ -1,468 +0,0 @@ -#import - -#ifndef __has_feature // Optional. -#define __has_feature(x) 0 // Compatibility with non-clang compilers. -#endif - -#ifndef NS_RETURNS_NOT_RETAINED -#if __has_feature(attribute_ns_returns_not_retained) -#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) -#else -#define NS_RETURNS_NOT_RETAINED -#endif -#endif - -@class FMDatabase; -@class FMStatement; - -/** Represents the results of executing a query on an ``. - - ### See also - - - `` - */ - -@interface FMResultSet : NSObject { - FMDatabase *_parentDB; - FMStatement *_statement; - - NSString *_query; - NSMutableDictionary *_columnNameToIndexMap; -} - -///----------------- -/// @name Properties -///----------------- - -/** Executed query */ - -@property (atomic, retain) NSString *query; - -/** `NSMutableDictionary` mapping column names to numeric index */ - -@property (readonly) NSMutableDictionary *columnNameToIndexMap; - -/** `FMStatement` used by result set. */ - -@property (atomic, retain) FMStatement *statement; - -///------------------------------------ -/// @name Creating and closing database -///------------------------------------ - -/** Create result set from `` - - @param statement A `` to be performed - - @param aDB A `` to be used - - @return A `FMResultSet` on success; `nil` on failure - */ - -+ (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB; - -/** Close result set */ - -- (void)close; - -- (void)setParentDB:(FMDatabase *)newDb; - -///--------------------------------------- -/// @name Iterating through the result set -///--------------------------------------- - -/** Retrieve next row for result set. - - You must always invoke `next` or `nextWithError` before attempting to access the values returned in a query, even if you're only expecting one. - - @return `YES` if row successfully retrieved; `NO` if end of result set reached - - @see hasAnotherRow - */ - -- (BOOL)next; - -/** Retrieve next row for result set. - - You must always invoke `next` or `nextWithError` before attempting to access the values returned in a query, even if you're only expecting one. - - @param outErr A 'NSError' object to receive any error object (if any). - - @return 'YES' if row successfully retrieved; 'NO' if end of result set reached - - @see hasAnotherRow - */ - -- (BOOL)nextWithError:(NSError **)outErr; - -/** Did the last call to `` succeed in retrieving another row? - - @return `YES` if the last call to `` succeeded in retrieving another record; `NO` if not. - - @see next - - @warning The `hasAnotherRow` method must follow a call to ``. If the previous database interaction was something other than a call to `next`, then this method may return `NO`, whether there is another row of data or not. - */ - -- (BOOL)hasAnotherRow; - -///--------------------------------------------- -/// @name Retrieving information from result set -///--------------------------------------------- - -/** How many columns in result set - - @return Integer value of the number of columns. - */ - -- (int)columnCount; - -/** Column index for column name - - @param columnName `NSString` value of the name of the column. - - @return Zero-based index for column. - */ - -- (int)columnIndexForName:(NSString*)columnName; - -/** Column name for column index - - @param columnIdx Zero-based index for column. - - @return columnName `NSString` value of the name of the column. - */ - -- (NSString*)columnNameForIndex:(int)columnIdx; - -/** Result set integer value for column. - - @param columnName `NSString` value of the name of the column. - - @return `int` value of the result set's column. - */ - -- (int)intForColumn:(NSString*)columnName; - -/** Result set integer value for column. - - @param columnIdx Zero-based index for column. - - @return `int` value of the result set's column. - */ - -- (int)intForColumnIndex:(int)columnIdx; - -/** Result set `long` value for column. - - @param columnName `NSString` value of the name of the column. - - @return `long` value of the result set's column. - */ - -- (long)longForColumn:(NSString*)columnName; - -/** Result set long value for column. - - @param columnIdx Zero-based index for column. - - @return `long` value of the result set's column. - */ - -- (long)longForColumnIndex:(int)columnIdx; - -/** Result set `long long int` value for column. - - @param columnName `NSString` value of the name of the column. - - @return `long long int` value of the result set's column. - */ - -- (long long int)longLongIntForColumn:(NSString*)columnName; - -/** Result set `long long int` value for column. - - @param columnIdx Zero-based index for column. - - @return `long long int` value of the result set's column. - */ - -- (long long int)longLongIntForColumnIndex:(int)columnIdx; - -/** Result set `unsigned long long int` value for column. - - @param columnName `NSString` value of the name of the column. - - @return `unsigned long long int` value of the result set's column. - */ - -- (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName; - -/** Result set `unsigned long long int` value for column. - - @param columnIdx Zero-based index for column. - - @return `unsigned long long int` value of the result set's column. - */ - -- (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx; - -/** Result set `BOOL` value for column. - - @param columnName `NSString` value of the name of the column. - - @return `BOOL` value of the result set's column. - */ - -- (BOOL)boolForColumn:(NSString*)columnName; - -/** Result set `BOOL` value for column. - - @param columnIdx Zero-based index for column. - - @return `BOOL` value of the result set's column. - */ - -- (BOOL)boolForColumnIndex:(int)columnIdx; - -/** Result set `double` value for column. - - @param columnName `NSString` value of the name of the column. - - @return `double` value of the result set's column. - - */ - -- (double)doubleForColumn:(NSString*)columnName; - -/** Result set `double` value for column. - - @param columnIdx Zero-based index for column. - - @return `double` value of the result set's column. - - */ - -- (double)doubleForColumnIndex:(int)columnIdx; - -/** Result set `NSString` value for column. - - @param columnName `NSString` value of the name of the column. - - @return `NSString` value of the result set's column. - - */ - -- (NSString*)stringForColumn:(NSString*)columnName; - -/** Result set `NSString` value for column. - - @param columnIdx Zero-based index for column. - - @return `NSString` value of the result set's column. - */ - -- (NSString*)stringForColumnIndex:(int)columnIdx; - -/** Result set `NSDate` value for column. - - @param columnName `NSString` value of the name of the column. - - @return `NSDate` value of the result set's column. - */ - -- (NSDate*)dateForColumn:(NSString*)columnName; - -/** Result set `NSDate` value for column. - - @param columnIdx Zero-based index for column. - - @return `NSDate` value of the result set's column. - - */ - -- (NSDate*)dateForColumnIndex:(int)columnIdx; - -/** Result set `NSData` value for column. - - This is useful when storing binary data in table (such as image or the like). - - @param columnName `NSString` value of the name of the column. - - @return `NSData` value of the result set's column. - - */ - -- (NSData*)dataForColumn:(NSString*)columnName; - -/** Result set `NSData` value for column. - - @param columnIdx Zero-based index for column. - - @return `NSData` value of the result set's column. - */ - -- (NSData*)dataForColumnIndex:(int)columnIdx; - -/** Result set `(const unsigned char *)` value for column. - - @param columnName `NSString` value of the name of the column. - - @return `(const unsigned char *)` value of the result set's column. - */ - -- (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName; - -/** Result set `(const unsigned char *)` value for column. - - @param columnIdx Zero-based index for column. - - @return `(const unsigned char *)` value of the result set's column. - */ - -- (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx; - -/** Result set object for column. - - @param columnName `NSString` value of the name of the column. - - @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. - - @see objectForKeyedSubscript: - */ - -- (id)objectForColumnName:(NSString*)columnName; - -/** Result set object for column. - - @param columnIdx Zero-based index for column. - - @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. - - @see objectAtIndexedSubscript: - */ - -- (id)objectForColumnIndex:(int)columnIdx; - -/** Result set object for column. - - This method allows the use of the "boxed" syntax supported in Modern Objective-C. For example, by defining this method, the following syntax is now supported: - - id result = rs[@"employee_name"]; - - This simplified syntax is equivalent to calling: - - id result = [rs objectForKeyedSubscript:@"employee_name"]; - - which is, it turns out, equivalent to calling: - - id result = [rs objectForColumnName:@"employee_name"]; - - @param columnName `NSString` value of the name of the column. - - @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. - */ - -- (id)objectForKeyedSubscript:(NSString *)columnName; - -/** Result set object for column. - - This method allows the use of the "boxed" syntax supported in Modern Objective-C. For example, by defining this method, the following syntax is now supported: - - id result = rs[0]; - - This simplified syntax is equivalent to calling: - - id result = [rs objectForKeyedSubscript:0]; - - which is, it turns out, equivalent to calling: - - id result = [rs objectForColumnName:0]; - - @param columnIdx Zero-based index for column. - - @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. - */ - -- (id)objectAtIndexedSubscript:(int)columnIdx; - -/** Result set `NSData` value for column. - - @param columnName `NSString` value of the name of the column. - - @return `NSData` value of the result set's column. - - @warning If you are going to use this data after you iterate over the next row, or after you close the -result set, make sure to make a copy of the data first (or just use ``/``) -If you don't, you're going to be in a world of hurt when you try and use the data. - - */ - -- (NSData*)dataNoCopyForColumn:(NSString*)columnName NS_RETURNS_NOT_RETAINED; - -/** Result set `NSData` value for column. - - @param columnIdx Zero-based index for column. - - @return `NSData` value of the result set's column. - - @warning If you are going to use this data after you iterate over the next row, or after you close the - result set, make sure to make a copy of the data first (or just use ``/``) - If you don't, you're going to be in a world of hurt when you try and use the data. - - */ - -- (NSData*)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED; - -/** Is the column `NULL`? - - @param columnIdx Zero-based index for column. - - @return `YES` if column is `NULL`; `NO` if not `NULL`. - */ - -- (BOOL)columnIndexIsNull:(int)columnIdx; - -/** Is the column `NULL`? - - @param columnName `NSString` value of the name of the column. - - @return `YES` if column is `NULL`; `NO` if not `NULL`. - */ - -- (BOOL)columnIsNull:(NSString*)columnName; - - -/** Returns a dictionary of the row results mapped to case sensitive keys of the column names. - - @returns `NSDictionary` of the row results. - - @warning The keys to the dictionary are case sensitive of the column names. - */ - -- (NSDictionary*)resultDictionary; - -/** Returns a dictionary of the row results - - @see resultDictionary - - @warning **Deprecated**: Please use `` instead. Also, beware that `` is case sensitive! - */ - -- (NSDictionary*)resultDict __attribute__ ((deprecated)); - -///----------------------------- -/// @name Key value coding magic -///----------------------------- - -/** Performs `setValue` to yield support for key value observing. - - @param object The object for which the values will be set. This is the key-value-coding compliant object that you might, for example, observe. - - */ - -- (void)kvcMagic:(id)object; - - -@end - diff --git a/Pods/FMDB/src/fmdb/FMResultSet.m b/Pods/FMDB/src/fmdb/FMResultSet.m deleted file mode 100644 index cfc51e1..0000000 --- a/Pods/FMDB/src/fmdb/FMResultSet.m +++ /dev/null @@ -1,422 +0,0 @@ -#import "FMResultSet.h" -#import "FMDatabase.h" -#import "unistd.h" - -#if FMDB_SQLITE_STANDALONE -#import -#else -#import -#endif - -@interface FMDatabase () -- (void)resultSetDidClose:(FMResultSet *)resultSet; -@end - - -@implementation FMResultSet -@synthesize query=_query; -@synthesize statement=_statement; - -+ (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB { - - FMResultSet *rs = [[FMResultSet alloc] init]; - - [rs setStatement:statement]; - [rs setParentDB:aDB]; - - NSParameterAssert(![statement inUse]); - [statement setInUse:YES]; // weak reference - - return FMDBReturnAutoreleased(rs); -} - -- (void)finalize { - [self close]; - [super finalize]; -} - -- (void)dealloc { - [self close]; - - FMDBRelease(_query); - _query = nil; - - FMDBRelease(_columnNameToIndexMap); - _columnNameToIndexMap = nil; - -#if ! __has_feature(objc_arc) - [super dealloc]; -#endif -} - -- (void)close { - [_statement reset]; - FMDBRelease(_statement); - _statement = nil; - - // we don't need this anymore... (i think) - //[_parentDB setInUse:NO]; - [_parentDB resultSetDidClose:self]; - _parentDB = nil; -} - -- (int)columnCount { - return sqlite3_column_count([_statement statement]); -} - -- (NSMutableDictionary *)columnNameToIndexMap { - if (!_columnNameToIndexMap) { - int columnCount = sqlite3_column_count([_statement statement]); - _columnNameToIndexMap = [[NSMutableDictionary alloc] initWithCapacity:(NSUInteger)columnCount]; - int columnIdx = 0; - for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { - [_columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx] - forKey:[[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)] lowercaseString]]; - } - } - return _columnNameToIndexMap; -} - -- (void)kvcMagic:(id)object { - - int columnCount = sqlite3_column_count([_statement statement]); - - int columnIdx = 0; - for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { - - const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); - - // check for a null row - if (c) { - NSString *s = [NSString stringWithUTF8String:c]; - - [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]]; - } - } -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" - -- (NSDictionary*)resultDict { - - NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); - - if (num_cols > 0) { - NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; - - NSEnumerator *columnNames = [[self columnNameToIndexMap] keyEnumerator]; - NSString *columnName = nil; - while ((columnName = [columnNames nextObject])) { - id objectValue = [self objectForColumnName:columnName]; - [dict setObject:objectValue forKey:columnName]; - } - - return FMDBReturnAutoreleased([dict copy]); - } - else { - NSLog(@"Warning: There seem to be no columns in this set."); - } - - return nil; -} - -#pragma clang diagnostic pop - -- (NSDictionary*)resultDictionary { - - NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); - - if (num_cols > 0) { - NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; - - int columnCount = sqlite3_column_count([_statement statement]); - - int columnIdx = 0; - for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { - - NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]; - id objectValue = [self objectForColumnIndex:columnIdx]; - [dict setObject:objectValue forKey:columnName]; - } - - return dict; - } - else { - NSLog(@"Warning: There seem to be no columns in this set."); - } - - return nil; -} - - - - -- (BOOL)next { - return [self nextWithError:nil]; -} - -- (BOOL)nextWithError:(NSError **)outErr { - - int rc = sqlite3_step([_statement statement]); - - if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { - NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]); - NSLog(@"Database busy"); - if (outErr) { - *outErr = [_parentDB lastError]; - } - } - else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { - // all is well, let's return. - } - else if (SQLITE_ERROR == rc) { - NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); - if (outErr) { - *outErr = [_parentDB lastError]; - } - } - else if (SQLITE_MISUSE == rc) { - // uh oh. - NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); - if (outErr) { - if (_parentDB) { - *outErr = [_parentDB lastError]; - } - else { - // If 'next' or 'nextWithError' is called after the result set is closed, - // we need to return the appropriate error. - NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:@"parentDB does not exist" forKey:NSLocalizedDescriptionKey]; - *outErr = [NSError errorWithDomain:@"FMDatabase" code:SQLITE_MISUSE userInfo:errorMessage]; - } - - } - } - else { - // wtf? - NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); - if (outErr) { - *outErr = [_parentDB lastError]; - } - } - - - if (rc != SQLITE_ROW) { - [self close]; - } - - return (rc == SQLITE_ROW); -} - -- (BOOL)hasAnotherRow { - return sqlite3_errcode([_parentDB sqliteHandle]) == SQLITE_ROW; -} - -- (int)columnIndexForName:(NSString*)columnName { - columnName = [columnName lowercaseString]; - - NSNumber *n = [[self columnNameToIndexMap] objectForKey:columnName]; - - if (n) { - return [n intValue]; - } - - NSLog(@"Warning: I could not find the column named '%@'.", columnName); - - return -1; -} - - - -- (int)intForColumn:(NSString*)columnName { - return [self intForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (int)intForColumnIndex:(int)columnIdx { - return sqlite3_column_int([_statement statement], columnIdx); -} - -- (long)longForColumn:(NSString*)columnName { - return [self longForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (long)longForColumnIndex:(int)columnIdx { - return (long)sqlite3_column_int64([_statement statement], columnIdx); -} - -- (long long int)longLongIntForColumn:(NSString*)columnName { - return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (long long int)longLongIntForColumnIndex:(int)columnIdx { - return sqlite3_column_int64([_statement statement], columnIdx); -} - -- (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName { - return [self unsignedLongLongIntForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx { - return (unsigned long long int)[self longLongIntForColumnIndex:columnIdx]; -} - -- (BOOL)boolForColumn:(NSString*)columnName { - return [self boolForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (BOOL)boolForColumnIndex:(int)columnIdx { - return ([self intForColumnIndex:columnIdx] != 0); -} - -- (double)doubleForColumn:(NSString*)columnName { - return [self doubleForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (double)doubleForColumnIndex:(int)columnIdx { - return sqlite3_column_double([_statement statement], columnIdx); -} - -- (NSString*)stringForColumnIndex:(int)columnIdx { - - if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { - return nil; - } - - const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); - - if (!c) { - // null row. - return nil; - } - - return [NSString stringWithUTF8String:c]; -} - -- (NSString*)stringForColumn:(NSString*)columnName { - return [self stringForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (NSDate*)dateForColumn:(NSString*)columnName { - return [self dateForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (NSDate*)dateForColumnIndex:(int)columnIdx { - - if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { - return nil; - } - - return [_parentDB hasDateFormatter] ? [_parentDB dateFromString:[self stringForColumnIndex:columnIdx]] : [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]]; -} - - -- (NSData*)dataForColumn:(NSString*)columnName { - return [self dataForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (NSData*)dataForColumnIndex:(int)columnIdx { - - if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { - return nil; - } - - const char *dataBuffer = sqlite3_column_blob([_statement statement], columnIdx); - int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); - - if (dataBuffer == NULL) { - return nil; - } - - return [NSData dataWithBytes:(const void *)dataBuffer length:(NSUInteger)dataSize]; -} - - -- (NSData*)dataNoCopyForColumn:(NSString*)columnName { - return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (NSData*)dataNoCopyForColumnIndex:(int)columnIdx { - - if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { - return nil; - } - - const char *dataBuffer = sqlite3_column_blob([_statement statement], columnIdx); - int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); - - NSData *data = [NSData dataWithBytesNoCopy:(void *)dataBuffer length:(NSUInteger)dataSize freeWhenDone:NO]; - - return data; -} - - -- (BOOL)columnIndexIsNull:(int)columnIdx { - return sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL; -} - -- (BOOL)columnIsNull:(NSString*)columnName { - return [self columnIndexIsNull:[self columnIndexForName:columnName]]; -} - -- (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx { - - if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { - return nil; - } - - return sqlite3_column_text([_statement statement], columnIdx); -} - -- (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName { - return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (id)objectForColumnIndex:(int)columnIdx { - int columnType = sqlite3_column_type([_statement statement], columnIdx); - - id returnValue = nil; - - if (columnType == SQLITE_INTEGER) { - returnValue = [NSNumber numberWithLongLong:[self longLongIntForColumnIndex:columnIdx]]; - } - else if (columnType == SQLITE_FLOAT) { - returnValue = [NSNumber numberWithDouble:[self doubleForColumnIndex:columnIdx]]; - } - else if (columnType == SQLITE_BLOB) { - returnValue = [self dataForColumnIndex:columnIdx]; - } - else { - //default to a string for everything else - returnValue = [self stringForColumnIndex:columnIdx]; - } - - if (returnValue == nil) { - returnValue = [NSNull null]; - } - - return returnValue; -} - -- (id)objectForColumnName:(NSString*)columnName { - return [self objectForColumnIndex:[self columnIndexForName:columnName]]; -} - -// returns autoreleased NSString containing the name of the column in the result set -- (NSString*)columnNameForIndex:(int)columnIdx { - return [NSString stringWithUTF8String: sqlite3_column_name([_statement statement], columnIdx)]; -} - -- (void)setParentDB:(FMDatabase *)newDb { - _parentDB = newDb; -} - -- (id)objectAtIndexedSubscript:(int)columnIdx { - return [self objectForColumnIndex:columnIdx]; -} - -- (id)objectForKeyedSubscript:(NSString *)columnName { - return [self objectForColumnName:columnName]; -} - - -@end diff --git a/Pods/Headers/Private/AFNetworking/AFAutoPurgingImageCache.h b/Pods/Headers/Private/AFNetworking/AFAutoPurgingImageCache.h deleted file mode 120000 index f9dc7db..0000000 --- a/Pods/Headers/Private/AFNetworking/AFAutoPurgingImageCache.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h b/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h deleted file mode 120000 index 56feb9f..0000000 --- a/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFHTTPSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFImageDownloader.h b/Pods/Headers/Private/AFNetworking/AFImageDownloader.h deleted file mode 120000 index ce47c92..0000000 --- a/Pods/Headers/Private/AFNetworking/AFImageDownloader.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/AFImageDownloader.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h deleted file mode 120000 index 67519d9..0000000 --- a/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h b/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h deleted file mode 120000 index 68fc774..0000000 --- a/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFNetworkReachabilityManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFNetworking.h b/Pods/Headers/Private/AFNetworking/AFNetworking.h deleted file mode 120000 index a5a38da..0000000 --- a/Pods/Headers/Private/AFNetworking/AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h b/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h deleted file mode 120000 index fd1322d..0000000 --- a/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFSecurityPolicy.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h b/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h deleted file mode 120000 index ca8209b..0000000 --- a/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFURLRequestSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h b/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h deleted file mode 120000 index e36a765..0000000 --- a/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFURLResponseSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h b/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h deleted file mode 120000 index 835101d..0000000 --- a/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFURLSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h deleted file mode 120000 index c534ebf..0000000 --- a/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h deleted file mode 120000 index 8f2e221..0000000 --- a/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIImage+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIImage+AFNetworking.h deleted file mode 120000 index 74f6649..0000000 --- a/Pods/Headers/Private/AFNetworking/UIImage+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h deleted file mode 120000 index a95d673..0000000 --- a/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h deleted file mode 120000 index 95017cc..0000000 --- a/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h deleted file mode 120000 index 730b167..0000000 --- a/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h deleted file mode 120000 index 8efd826..0000000 --- a/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h deleted file mode 120000 index c8df6ef..0000000 --- a/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/DACircularProgress/DACircularProgressView.h b/Pods/Headers/Private/DACircularProgress/DACircularProgressView.h deleted file mode 120000 index b67e984..0000000 --- a/Pods/Headers/Private/DACircularProgress/DACircularProgressView.h +++ /dev/null @@ -1 +0,0 @@ -../../../DACircularProgress/DACircularProgress/DACircularProgressView.h \ No newline at end of file diff --git a/Pods/Headers/Private/DACircularProgress/DALabeledCircularProgressView.h b/Pods/Headers/Private/DACircularProgress/DALabeledCircularProgressView.h deleted file mode 120000 index 89a694f..0000000 --- a/Pods/Headers/Private/DACircularProgress/DALabeledCircularProgressView.h +++ /dev/null @@ -1 +0,0 @@ -../../../DACircularProgress/DACircularProgress/DALabeledCircularProgressView.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/CALayer+Night.h b/Pods/Headers/Private/DKNightVersion/CALayer+Night.h deleted file mode 120000 index a76efa6..0000000 --- a/Pods/Headers/Private/DKNightVersion/CALayer+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/CoreAnimation/CALayer+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/CoreAnimation+Night.h b/Pods/Headers/Private/DKNightVersion/CoreAnimation+Night.h deleted file mode 120000 index bc55873..0000000 --- a/Pods/Headers/Private/DKNightVersion/CoreAnimation+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/CoreAnimation/CoreAnimation+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/DKColor.h b/Pods/Headers/Private/DKNightVersion/DKColor.h deleted file mode 120000 index 555efac..0000000 --- a/Pods/Headers/Private/DKNightVersion/DKColor.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Core/DKColor.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/DKColorTable.h b/Pods/Headers/Private/DKNightVersion/DKColorTable.h deleted file mode 120000 index 03fe7d2..0000000 --- a/Pods/Headers/Private/DKNightVersion/DKColorTable.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/ColorTable/DKColorTable.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/DKDeallocBlockExecutor.h b/Pods/Headers/Private/DKNightVersion/DKDeallocBlockExecutor.h deleted file mode 120000 index 7893cb1..0000000 --- a/Pods/Headers/Private/DKNightVersion/DKDeallocBlockExecutor.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/DeallocBlockExecutor/DKDeallocBlockExecutor.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/DKImage.h b/Pods/Headers/Private/DKNightVersion/DKImage.h deleted file mode 120000 index f815ac0..0000000 --- a/Pods/Headers/Private/DKNightVersion/DKImage.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Core/DKImage.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/DKNightVersion.h b/Pods/Headers/Private/DKNightVersion/DKNightVersion.h deleted file mode 120000 index 83411fb..0000000 --- a/Pods/Headers/Private/DKNightVersion/DKNightVersion.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/DKNightVersion.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/DKNightVersionManager.h b/Pods/Headers/Private/DKNightVersion/DKNightVersionManager.h deleted file mode 120000 index 6255e9d..0000000 --- a/Pods/Headers/Private/DKNightVersion/DKNightVersionManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Core/DKNightVersionManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/EXTKeyPathCoding.h b/Pods/Headers/Private/DKNightVersion/EXTKeyPathCoding.h deleted file mode 120000 index 10bedf6..0000000 --- a/Pods/Headers/Private/DKNightVersion/EXTKeyPathCoding.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/extobjc/EXTKeyPathCoding.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/NSObject+DeallocBlock.h b/Pods/Headers/Private/DKNightVersion/NSObject+DeallocBlock.h deleted file mode 120000 index a7bdc4d..0000000 --- a/Pods/Headers/Private/DKNightVersion/NSObject+DeallocBlock.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/DeallocBlockExecutor/NSObject+DeallocBlock.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/NSObject+Night.h b/Pods/Headers/Private/DKNightVersion/NSObject+Night.h deleted file mode 120000 index 34fe423..0000000 --- a/Pods/Headers/Private/DKNightVersion/NSObject+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Core/NSObject+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UIBarButtonItem+Night.h b/Pods/Headers/Private/DKNightVersion/UIBarButtonItem+Night.h deleted file mode 120000 index b00dcaf..0000000 --- a/Pods/Headers/Private/DKNightVersion/UIBarButtonItem+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIBarButtonItem+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UIButton+Night.h b/Pods/Headers/Private/DKNightVersion/UIButton+Night.h deleted file mode 120000 index 83e7442..0000000 --- a/Pods/Headers/Private/DKNightVersion/UIButton+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Manual/UIButton+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UIControl+Night.h b/Pods/Headers/Private/DKNightVersion/UIControl+Night.h deleted file mode 120000 index 229825e..0000000 --- a/Pods/Headers/Private/DKNightVersion/UIControl+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIControl+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UIImageView+Night.h b/Pods/Headers/Private/DKNightVersion/UIImageView+Night.h deleted file mode 120000 index 8605ee8..0000000 --- a/Pods/Headers/Private/DKNightVersion/UIImageView+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Manual/UIImageView+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UILabel+Night.h b/Pods/Headers/Private/DKNightVersion/UILabel+Night.h deleted file mode 120000 index bd8fddb..0000000 --- a/Pods/Headers/Private/DKNightVersion/UILabel+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UILabel+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UINavigationBar+Animation.h b/Pods/Headers/Private/DKNightVersion/UINavigationBar+Animation.h deleted file mode 120000 index ea11500..0000000 --- a/Pods/Headers/Private/DKNightVersion/UINavigationBar+Animation.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Manual/UINavigationBar+Animation.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UINavigationBar+Night.h b/Pods/Headers/Private/DKNightVersion/UINavigationBar+Night.h deleted file mode 120000 index b5e8787..0000000 --- a/Pods/Headers/Private/DKNightVersion/UINavigationBar+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UINavigationBar+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UIPageControl+Night.h b/Pods/Headers/Private/DKNightVersion/UIPageControl+Night.h deleted file mode 120000 index 890b0eb..0000000 --- a/Pods/Headers/Private/DKNightVersion/UIPageControl+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIPageControl+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UIProgressView+Night.h b/Pods/Headers/Private/DKNightVersion/UIProgressView+Night.h deleted file mode 120000 index 4fa22ef..0000000 --- a/Pods/Headers/Private/DKNightVersion/UIProgressView+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIProgressView+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UISearchBar+Night.h b/Pods/Headers/Private/DKNightVersion/UISearchBar+Night.h deleted file mode 120000 index b06aeee..0000000 --- a/Pods/Headers/Private/DKNightVersion/UISearchBar+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UISearchBar+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UISlider+Night.h b/Pods/Headers/Private/DKNightVersion/UISlider+Night.h deleted file mode 120000 index 336ae17..0000000 --- a/Pods/Headers/Private/DKNightVersion/UISlider+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UISlider+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UISwitch+Night.h b/Pods/Headers/Private/DKNightVersion/UISwitch+Night.h deleted file mode 120000 index 1951335..0000000 --- a/Pods/Headers/Private/DKNightVersion/UISwitch+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UISwitch+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UITabBar+Night.h b/Pods/Headers/Private/DKNightVersion/UITabBar+Night.h deleted file mode 120000 index 4a8cc5a..0000000 --- a/Pods/Headers/Private/DKNightVersion/UITabBar+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UITabBar+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UITableView+Night.h b/Pods/Headers/Private/DKNightVersion/UITableView+Night.h deleted file mode 120000 index 96f6423..0000000 --- a/Pods/Headers/Private/DKNightVersion/UITableView+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UITableView+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UITextField+Keyboard.h b/Pods/Headers/Private/DKNightVersion/UITextField+Keyboard.h deleted file mode 120000 index 39df51f..0000000 --- a/Pods/Headers/Private/DKNightVersion/UITextField+Keyboard.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Manual/UITextField+Keyboard.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UITextField+Night.h b/Pods/Headers/Private/DKNightVersion/UITextField+Night.h deleted file mode 120000 index 7da49ae..0000000 --- a/Pods/Headers/Private/DKNightVersion/UITextField+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UITextField+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UITextView+Night.h b/Pods/Headers/Private/DKNightVersion/UITextView+Night.h deleted file mode 120000 index e2adda5..0000000 --- a/Pods/Headers/Private/DKNightVersion/UITextView+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UITextView+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UIToolbar+Night.h b/Pods/Headers/Private/DKNightVersion/UIToolbar+Night.h deleted file mode 120000 index 0a42d6e..0000000 --- a/Pods/Headers/Private/DKNightVersion/UIToolbar+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIToolbar+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/UIView+Night.h b/Pods/Headers/Private/DKNightVersion/UIView+Night.h deleted file mode 120000 index af0f2db..0000000 --- a/Pods/Headers/Private/DKNightVersion/UIView+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIView+Night.h \ No newline at end of file diff --git a/Pods/Headers/Private/DKNightVersion/metamacros.h b/Pods/Headers/Private/DKNightVersion/metamacros.h deleted file mode 120000 index e8fe88b..0000000 --- a/Pods/Headers/Private/DKNightVersion/metamacros.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/extobjc/metamacros.h \ No newline at end of file diff --git a/Pods/Headers/Private/FMDB/FMDB.h b/Pods/Headers/Private/FMDB/FMDB.h deleted file mode 120000 index bcd6e0a..0000000 --- a/Pods/Headers/Private/FMDB/FMDB.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMDB.h \ No newline at end of file diff --git a/Pods/Headers/Private/FMDB/FMDatabase.h b/Pods/Headers/Private/FMDB/FMDatabase.h deleted file mode 120000 index e69b333..0000000 --- a/Pods/Headers/Private/FMDB/FMDatabase.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMDatabase.h \ No newline at end of file diff --git a/Pods/Headers/Private/FMDB/FMDatabaseAdditions.h b/Pods/Headers/Private/FMDB/FMDatabaseAdditions.h deleted file mode 120000 index b48a6a3..0000000 --- a/Pods/Headers/Private/FMDB/FMDatabaseAdditions.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMDatabaseAdditions.h \ No newline at end of file diff --git a/Pods/Headers/Private/FMDB/FMDatabasePool.h b/Pods/Headers/Private/FMDB/FMDatabasePool.h deleted file mode 120000 index 1d78001..0000000 --- a/Pods/Headers/Private/FMDB/FMDatabasePool.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMDatabasePool.h \ No newline at end of file diff --git a/Pods/Headers/Private/FMDB/FMDatabaseQueue.h b/Pods/Headers/Private/FMDB/FMDatabaseQueue.h deleted file mode 120000 index 9adde87..0000000 --- a/Pods/Headers/Private/FMDB/FMDatabaseQueue.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMDatabaseQueue.h \ No newline at end of file diff --git a/Pods/Headers/Private/FMDB/FMResultSet.h b/Pods/Headers/Private/FMDB/FMResultSet.h deleted file mode 120000 index fd761d8..0000000 --- a/Pods/Headers/Private/FMDB/FMResultSet.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMResultSet.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJExtension.h b/Pods/Headers/Private/MJExtension/MJExtension.h deleted file mode 120000 index 1df1880..0000000 --- a/Pods/Headers/Private/MJExtension/MJExtension.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJExtensionConst.h b/Pods/Headers/Private/MJExtension/MJExtensionConst.h deleted file mode 120000 index f1a01cd..0000000 --- a/Pods/Headers/Private/MJExtension/MJExtensionConst.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJExtensionConst.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJFoundation.h b/Pods/Headers/Private/MJExtension/MJFoundation.h deleted file mode 120000 index 7c66d2f..0000000 --- a/Pods/Headers/Private/MJExtension/MJFoundation.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJFoundation.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJProperty.h b/Pods/Headers/Private/MJExtension/MJProperty.h deleted file mode 120000 index 5361eff..0000000 --- a/Pods/Headers/Private/MJExtension/MJProperty.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJProperty.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJPropertyKey.h b/Pods/Headers/Private/MJExtension/MJPropertyKey.h deleted file mode 120000 index d740b30..0000000 --- a/Pods/Headers/Private/MJExtension/MJPropertyKey.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJPropertyKey.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJPropertyType.h b/Pods/Headers/Private/MJExtension/MJPropertyType.h deleted file mode 120000 index 3ab7735..0000000 --- a/Pods/Headers/Private/MJExtension/MJPropertyType.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJPropertyType.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/NSObject+MJClass.h b/Pods/Headers/Private/MJExtension/NSObject+MJClass.h deleted file mode 120000 index 9dde422..0000000 --- a/Pods/Headers/Private/MJExtension/NSObject+MJClass.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/NSObject+MJClass.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/NSObject+MJCoding.h b/Pods/Headers/Private/MJExtension/NSObject+MJCoding.h deleted file mode 120000 index 8f4f799..0000000 --- a/Pods/Headers/Private/MJExtension/NSObject+MJCoding.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/NSObject+MJCoding.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/NSObject+MJKeyValue.h b/Pods/Headers/Private/MJExtension/NSObject+MJKeyValue.h deleted file mode 120000 index 9d5b17d..0000000 --- a/Pods/Headers/Private/MJExtension/NSObject+MJKeyValue.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/NSObject+MJKeyValue.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/NSObject+MJProperty.h b/Pods/Headers/Private/MJExtension/NSObject+MJProperty.h deleted file mode 120000 index 2ee634e..0000000 --- a/Pods/Headers/Private/MJExtension/NSObject+MJProperty.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/NSObject+MJProperty.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/NSString+MJExtension.h b/Pods/Headers/Private/MJExtension/NSString+MJExtension.h deleted file mode 120000 index 583ce69..0000000 --- a/Pods/Headers/Private/MJExtension/NSString+MJExtension.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/NSString+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefresh.h b/Pods/Headers/Private/MJRefresh/MJRefresh.h deleted file mode 120000 index d83fdd6..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefresh.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshAutoFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshAutoFooter.h deleted file mode 120000 index 5bb1fbc..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshAutoFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshAutoGifFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshAutoGifFooter.h deleted file mode 120000 index 1435437..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshAutoGifFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshAutoNormalFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshAutoNormalFooter.h deleted file mode 120000 index 6b87abe..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshAutoNormalFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshAutoStateFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshAutoStateFooter.h deleted file mode 120000 index 19b2053..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshAutoStateFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshBackFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshBackFooter.h deleted file mode 120000 index 3df4973..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshBackFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshBackGifFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshBackGifFooter.h deleted file mode 120000 index e5d5f96..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshBackGifFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshBackNormalFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshBackNormalFooter.h deleted file mode 120000 index c578c7f..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshBackNormalFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshBackStateFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshBackStateFooter.h deleted file mode 120000 index 3317188..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshBackStateFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshComponent.h b/Pods/Headers/Private/MJRefresh/MJRefreshComponent.h deleted file mode 120000 index 35cb1b1..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshComponent.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Base/MJRefreshComponent.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshConst.h b/Pods/Headers/Private/MJRefresh/MJRefreshConst.h deleted file mode 120000 index 035954b..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshConst.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/MJRefreshConst.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshFooter.h deleted file mode 120000 index 743dda2..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Base/MJRefreshFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshGifHeader.h b/Pods/Headers/Private/MJRefresh/MJRefreshGifHeader.h deleted file mode 120000 index aa6afbd..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshGifHeader.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshHeader.h b/Pods/Headers/Private/MJRefresh/MJRefreshHeader.h deleted file mode 120000 index 3a38b26..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshHeader.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Base/MJRefreshHeader.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshNormalHeader.h b/Pods/Headers/Private/MJRefresh/MJRefreshNormalHeader.h deleted file mode 120000 index b5cc164..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshNormalHeader.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshStateHeader.h b/Pods/Headers/Private/MJRefresh/MJRefreshStateHeader.h deleted file mode 120000 index 7bd25cf..0000000 --- a/Pods/Headers/Private/MJRefresh/MJRefreshStateHeader.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/UIScrollView+MJExtension.h b/Pods/Headers/Private/MJRefresh/UIScrollView+MJExtension.h deleted file mode 120000 index 1919784..0000000 --- a/Pods/Headers/Private/MJRefresh/UIScrollView+MJExtension.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/UIScrollView+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/UIScrollView+MJRefresh.h b/Pods/Headers/Private/MJRefresh/UIScrollView+MJRefresh.h deleted file mode 120000 index 47b703d..0000000 --- a/Pods/Headers/Private/MJRefresh/UIScrollView+MJRefresh.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/UIScrollView+MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/UIView+MJExtension.h b/Pods/Headers/Private/MJRefresh/UIView+MJExtension.h deleted file mode 120000 index 5f19813..0000000 --- a/Pods/Headers/Private/MJRefresh/UIView+MJExtension.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/UIView+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Private/NJKWebViewProgress/NJKWebViewProgress.h b/Pods/Headers/Private/NJKWebViewProgress/NJKWebViewProgress.h deleted file mode 120000 index 532f6eb..0000000 --- a/Pods/Headers/Private/NJKWebViewProgress/NJKWebViewProgress.h +++ /dev/null @@ -1 +0,0 @@ -../../../NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgress.h \ No newline at end of file diff --git a/Pods/Headers/Private/NJKWebViewProgress/NJKWebViewProgressView.h b/Pods/Headers/Private/NJKWebViewProgress/NJKWebViewProgressView.h deleted file mode 120000 index 58af127..0000000 --- a/Pods/Headers/Private/NJKWebViewProgress/NJKWebViewProgressView.h +++ /dev/null @@ -1 +0,0 @@ -../../../NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgressView.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/NSData+ImageContentType.h b/Pods/Headers/Private/SDWebImage/NSData+ImageContentType.h deleted file mode 120000 index 8457498..0000000 --- a/Pods/Headers/Private/SDWebImage/NSData+ImageContentType.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/NSData+ImageContentType.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/SDImageCache.h b/Pods/Headers/Private/SDWebImage/SDImageCache.h deleted file mode 120000 index 0040b06..0000000 --- a/Pods/Headers/Private/SDWebImage/SDImageCache.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDImageCache.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/SDWebImageCompat.h b/Pods/Headers/Private/SDWebImage/SDWebImageCompat.h deleted file mode 120000 index 6ca2478..0000000 --- a/Pods/Headers/Private/SDWebImage/SDWebImageCompat.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageCompat.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/SDWebImageDecoder.h b/Pods/Headers/Private/SDWebImage/SDWebImageDecoder.h deleted file mode 120000 index a2f3a68..0000000 --- a/Pods/Headers/Private/SDWebImage/SDWebImageDecoder.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageDecoder.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/SDWebImageDownloader.h b/Pods/Headers/Private/SDWebImage/SDWebImageDownloader.h deleted file mode 120000 index 303b03b..0000000 --- a/Pods/Headers/Private/SDWebImage/SDWebImageDownloader.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageDownloader.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/SDWebImageDownloaderOperation.h b/Pods/Headers/Private/SDWebImage/SDWebImageDownloaderOperation.h deleted file mode 120000 index 99441c4..0000000 --- a/Pods/Headers/Private/SDWebImage/SDWebImageDownloaderOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/SDWebImageManager.h b/Pods/Headers/Private/SDWebImage/SDWebImageManager.h deleted file mode 120000 index 1b81848..0000000 --- a/Pods/Headers/Private/SDWebImage/SDWebImageManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/SDWebImageOperation.h b/Pods/Headers/Private/SDWebImage/SDWebImageOperation.h deleted file mode 120000 index 20e5b89..0000000 --- a/Pods/Headers/Private/SDWebImage/SDWebImageOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageOperation.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/SDWebImagePrefetcher.h b/Pods/Headers/Private/SDWebImage/SDWebImagePrefetcher.h deleted file mode 120000 index 50585c6..0000000 --- a/Pods/Headers/Private/SDWebImage/SDWebImagePrefetcher.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImagePrefetcher.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/UIButton+WebCache.h b/Pods/Headers/Private/SDWebImage/UIButton+WebCache.h deleted file mode 120000 index 19d2d8e..0000000 --- a/Pods/Headers/Private/SDWebImage/UIButton+WebCache.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIButton+WebCache.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/UIImage+GIF.h b/Pods/Headers/Private/SDWebImage/UIImage+GIF.h deleted file mode 120000 index 14d5aad..0000000 --- a/Pods/Headers/Private/SDWebImage/UIImage+GIF.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIImage+GIF.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/UIImage+MultiFormat.h b/Pods/Headers/Private/SDWebImage/UIImage+MultiFormat.h deleted file mode 120000 index 1fb9650..0000000 --- a/Pods/Headers/Private/SDWebImage/UIImage+MultiFormat.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIImage+MultiFormat.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/UIImageView+HighlightedWebCache.h b/Pods/Headers/Private/SDWebImage/UIImageView+HighlightedWebCache.h deleted file mode 120000 index fd4dea4..0000000 --- a/Pods/Headers/Private/SDWebImage/UIImageView+HighlightedWebCache.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/UIImageView+WebCache.h b/Pods/Headers/Private/SDWebImage/UIImageView+WebCache.h deleted file mode 120000 index 0c53a47..0000000 --- a/Pods/Headers/Private/SDWebImage/UIImageView+WebCache.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIImageView+WebCache.h \ No newline at end of file diff --git a/Pods/Headers/Private/SDWebImage/UIView+WebCacheOperation.h b/Pods/Headers/Private/SDWebImage/UIView+WebCacheOperation.h deleted file mode 120000 index f9890c4..0000000 --- a/Pods/Headers/Private/SDWebImage/UIView+WebCacheOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIView+WebCacheOperation.h \ No newline at end of file diff --git a/Pods/Headers/Private/SVProgressHUD/SVIndefiniteAnimatedView.h b/Pods/Headers/Private/SVProgressHUD/SVIndefiniteAnimatedView.h deleted file mode 120000 index 55a38a2..0000000 --- a/Pods/Headers/Private/SVProgressHUD/SVIndefiniteAnimatedView.h +++ /dev/null @@ -1 +0,0 @@ -../../../SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.h \ No newline at end of file diff --git a/Pods/Headers/Private/SVProgressHUD/SVProgressAnimatedView.h b/Pods/Headers/Private/SVProgressHUD/SVProgressAnimatedView.h deleted file mode 120000 index 581cdc2..0000000 --- a/Pods/Headers/Private/SVProgressHUD/SVProgressAnimatedView.h +++ /dev/null @@ -1 +0,0 @@ -../../../SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.h \ No newline at end of file diff --git a/Pods/Headers/Private/SVProgressHUD/SVProgressHUD.h b/Pods/Headers/Private/SVProgressHUD/SVProgressHUD.h deleted file mode 120000 index 608a8aa..0000000 --- a/Pods/Headers/Private/SVProgressHUD/SVProgressHUD.h +++ /dev/null @@ -1 +0,0 @@ -../../../SVProgressHUD/SVProgressHUD/SVProgressHUD.h \ No newline at end of file diff --git a/Pods/Headers/Private/SVProgressHUD/SVRadialGradientLayer.h b/Pods/Headers/Private/SVProgressHUD/SVRadialGradientLayer.h deleted file mode 120000 index d78beb5..0000000 --- a/Pods/Headers/Private/SVProgressHUD/SVRadialGradientLayer.h +++ /dev/null @@ -1 +0,0 @@ -../../../SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/FloatConversion.h b/Pods/Headers/Private/pop/FloatConversion.h deleted file mode 120000 index aea012a..0000000 --- a/Pods/Headers/Private/pop/FloatConversion.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/WebCore/FloatConversion.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POP.h b/Pods/Headers/Private/pop/POP.h deleted file mode 120000 index dd15660..0000000 --- a/Pods/Headers/Private/pop/POP.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POP.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAction.h b/Pods/Headers/Private/pop/POPAction.h deleted file mode 120000 index 78f9372..0000000 --- a/Pods/Headers/Private/pop/POPAction.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAction.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimatableProperty.h b/Pods/Headers/Private/pop/POPAnimatableProperty.h deleted file mode 120000 index 48fd8c4..0000000 --- a/Pods/Headers/Private/pop/POPAnimatableProperty.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimatableProperty.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimation.h b/Pods/Headers/Private/pop/POPAnimation.h deleted file mode 120000 index dfe8a85..0000000 --- a/Pods/Headers/Private/pop/POPAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimationEvent.h b/Pods/Headers/Private/pop/POPAnimationEvent.h deleted file mode 120000 index 5d40492..0000000 --- a/Pods/Headers/Private/pop/POPAnimationEvent.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationEvent.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimationEventInternal.h b/Pods/Headers/Private/pop/POPAnimationEventInternal.h deleted file mode 120000 index 8263df6..0000000 --- a/Pods/Headers/Private/pop/POPAnimationEventInternal.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationEventInternal.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimationExtras.h b/Pods/Headers/Private/pop/POPAnimationExtras.h deleted file mode 120000 index 3a1bc7e..0000000 --- a/Pods/Headers/Private/pop/POPAnimationExtras.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationExtras.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimationInternal.h b/Pods/Headers/Private/pop/POPAnimationInternal.h deleted file mode 120000 index 6aebf45..0000000 --- a/Pods/Headers/Private/pop/POPAnimationInternal.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationInternal.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimationPrivate.h b/Pods/Headers/Private/pop/POPAnimationPrivate.h deleted file mode 120000 index ea5956d..0000000 --- a/Pods/Headers/Private/pop/POPAnimationPrivate.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimationRuntime.h b/Pods/Headers/Private/pop/POPAnimationRuntime.h deleted file mode 120000 index 0651d06..0000000 --- a/Pods/Headers/Private/pop/POPAnimationRuntime.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationRuntime.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimationTracer.h b/Pods/Headers/Private/pop/POPAnimationTracer.h deleted file mode 120000 index 64fff66..0000000 --- a/Pods/Headers/Private/pop/POPAnimationTracer.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationTracer.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimationTracerInternal.h b/Pods/Headers/Private/pop/POPAnimationTracerInternal.h deleted file mode 120000 index b76b731..0000000 --- a/Pods/Headers/Private/pop/POPAnimationTracerInternal.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationTracerInternal.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimator.h b/Pods/Headers/Private/pop/POPAnimator.h deleted file mode 120000 index 89707ea..0000000 --- a/Pods/Headers/Private/pop/POPAnimator.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimator.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPAnimatorPrivate.h b/Pods/Headers/Private/pop/POPAnimatorPrivate.h deleted file mode 120000 index 89650c6..0000000 --- a/Pods/Headers/Private/pop/POPAnimatorPrivate.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimatorPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPBasicAnimation.h b/Pods/Headers/Private/pop/POPBasicAnimation.h deleted file mode 120000 index 50184f2..0000000 --- a/Pods/Headers/Private/pop/POPBasicAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPBasicAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPBasicAnimationInternal.h b/Pods/Headers/Private/pop/POPBasicAnimationInternal.h deleted file mode 120000 index fa8cb0b..0000000 --- a/Pods/Headers/Private/pop/POPBasicAnimationInternal.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPBasicAnimationInternal.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPCGUtils.h b/Pods/Headers/Private/pop/POPCGUtils.h deleted file mode 120000 index d050fb4..0000000 --- a/Pods/Headers/Private/pop/POPCGUtils.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPCGUtils.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPCustomAnimation.h b/Pods/Headers/Private/pop/POPCustomAnimation.h deleted file mode 120000 index 5025bd6..0000000 --- a/Pods/Headers/Private/pop/POPCustomAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPCustomAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPDecayAnimation.h b/Pods/Headers/Private/pop/POPDecayAnimation.h deleted file mode 120000 index dba2796..0000000 --- a/Pods/Headers/Private/pop/POPDecayAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPDecayAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPDecayAnimationInternal.h b/Pods/Headers/Private/pop/POPDecayAnimationInternal.h deleted file mode 120000 index 4d5c959..0000000 --- a/Pods/Headers/Private/pop/POPDecayAnimationInternal.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPDecayAnimationInternal.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPDefines.h b/Pods/Headers/Private/pop/POPDefines.h deleted file mode 120000 index 4b10036..0000000 --- a/Pods/Headers/Private/pop/POPDefines.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPDefines.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPGeometry.h b/Pods/Headers/Private/pop/POPGeometry.h deleted file mode 120000 index 9b29189..0000000 --- a/Pods/Headers/Private/pop/POPGeometry.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPGeometry.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPLayerExtras.h b/Pods/Headers/Private/pop/POPLayerExtras.h deleted file mode 120000 index e11f4c6..0000000 --- a/Pods/Headers/Private/pop/POPLayerExtras.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPLayerExtras.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPMath.h b/Pods/Headers/Private/pop/POPMath.h deleted file mode 120000 index cc52b21..0000000 --- a/Pods/Headers/Private/pop/POPMath.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPMath.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPPropertyAnimation.h b/Pods/Headers/Private/pop/POPPropertyAnimation.h deleted file mode 120000 index 0fae4c5..0000000 --- a/Pods/Headers/Private/pop/POPPropertyAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPPropertyAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPPropertyAnimationInternal.h b/Pods/Headers/Private/pop/POPPropertyAnimationInternal.h deleted file mode 120000 index 5783767..0000000 --- a/Pods/Headers/Private/pop/POPPropertyAnimationInternal.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPPropertyAnimationInternal.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPSpringAnimation.h b/Pods/Headers/Private/pop/POPSpringAnimation.h deleted file mode 120000 index 152f663..0000000 --- a/Pods/Headers/Private/pop/POPSpringAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPSpringAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPSpringAnimationInternal.h b/Pods/Headers/Private/pop/POPSpringAnimationInternal.h deleted file mode 120000 index afdc982..0000000 --- a/Pods/Headers/Private/pop/POPSpringAnimationInternal.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPSpringAnimationInternal.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPSpringSolver.h b/Pods/Headers/Private/pop/POPSpringSolver.h deleted file mode 120000 index 6ed1ee6..0000000 --- a/Pods/Headers/Private/pop/POPSpringSolver.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPSpringSolver.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/POPVector.h b/Pods/Headers/Private/pop/POPVector.h deleted file mode 120000 index 73f01f9..0000000 --- a/Pods/Headers/Private/pop/POPVector.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPVector.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/TransformationMatrix.h b/Pods/Headers/Private/pop/TransformationMatrix.h deleted file mode 120000 index f1232b9..0000000 --- a/Pods/Headers/Private/pop/TransformationMatrix.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/WebCore/TransformationMatrix.h \ No newline at end of file diff --git a/Pods/Headers/Private/pop/UnitBezier.h b/Pods/Headers/Private/pop/UnitBezier.h deleted file mode 120000 index fbd11a0..0000000 --- a/Pods/Headers/Private/pop/UnitBezier.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/WebCore/UnitBezier.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFAutoPurgingImageCache.h b/Pods/Headers/Public/AFNetworking/AFAutoPurgingImageCache.h deleted file mode 120000 index f9dc7db..0000000 --- a/Pods/Headers/Public/AFNetworking/AFAutoPurgingImageCache.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h b/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h deleted file mode 120000 index 56feb9f..0000000 --- a/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFHTTPSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFImageDownloader.h b/Pods/Headers/Public/AFNetworking/AFImageDownloader.h deleted file mode 120000 index ce47c92..0000000 --- a/Pods/Headers/Public/AFNetworking/AFImageDownloader.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/AFImageDownloader.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h deleted file mode 120000 index 67519d9..0000000 --- a/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h b/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h deleted file mode 120000 index 68fc774..0000000 --- a/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFNetworkReachabilityManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFNetworking.h b/Pods/Headers/Public/AFNetworking/AFNetworking.h deleted file mode 120000 index a5a38da..0000000 --- a/Pods/Headers/Public/AFNetworking/AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h b/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h deleted file mode 120000 index fd1322d..0000000 --- a/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFSecurityPolicy.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h b/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h deleted file mode 120000 index ca8209b..0000000 --- a/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFURLRequestSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h b/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h deleted file mode 120000 index e36a765..0000000 --- a/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFURLResponseSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h b/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h deleted file mode 120000 index 835101d..0000000 --- a/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/AFNetworking/AFURLSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h deleted file mode 120000 index c534ebf..0000000 --- a/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h deleted file mode 120000 index 8f2e221..0000000 --- a/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIImage+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIImage+AFNetworking.h deleted file mode 120000 index 74f6649..0000000 --- a/Pods/Headers/Public/AFNetworking/UIImage+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h deleted file mode 120000 index a95d673..0000000 --- a/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h deleted file mode 120000 index 95017cc..0000000 --- a/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h deleted file mode 120000 index 730b167..0000000 --- a/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h deleted file mode 120000 index 8efd826..0000000 --- a/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h deleted file mode 120000 index c8df6ef..0000000 --- a/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h +++ /dev/null @@ -1 +0,0 @@ -../../../AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/DACircularProgress/DACircularProgressView.h b/Pods/Headers/Public/DACircularProgress/DACircularProgressView.h deleted file mode 120000 index b67e984..0000000 --- a/Pods/Headers/Public/DACircularProgress/DACircularProgressView.h +++ /dev/null @@ -1 +0,0 @@ -../../../DACircularProgress/DACircularProgress/DACircularProgressView.h \ No newline at end of file diff --git a/Pods/Headers/Public/DACircularProgress/DALabeledCircularProgressView.h b/Pods/Headers/Public/DACircularProgress/DALabeledCircularProgressView.h deleted file mode 120000 index 89a694f..0000000 --- a/Pods/Headers/Public/DACircularProgress/DALabeledCircularProgressView.h +++ /dev/null @@ -1 +0,0 @@ -../../../DACircularProgress/DACircularProgress/DALabeledCircularProgressView.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/CALayer+Night.h b/Pods/Headers/Public/DKNightVersion/CALayer+Night.h deleted file mode 120000 index a76efa6..0000000 --- a/Pods/Headers/Public/DKNightVersion/CALayer+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/CoreAnimation/CALayer+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/CoreAnimation+Night.h b/Pods/Headers/Public/DKNightVersion/CoreAnimation+Night.h deleted file mode 120000 index bc55873..0000000 --- a/Pods/Headers/Public/DKNightVersion/CoreAnimation+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/CoreAnimation/CoreAnimation+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/DKColor.h b/Pods/Headers/Public/DKNightVersion/DKColor.h deleted file mode 120000 index 555efac..0000000 --- a/Pods/Headers/Public/DKNightVersion/DKColor.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Core/DKColor.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/DKColorTable.h b/Pods/Headers/Public/DKNightVersion/DKColorTable.h deleted file mode 120000 index 03fe7d2..0000000 --- a/Pods/Headers/Public/DKNightVersion/DKColorTable.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/ColorTable/DKColorTable.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/DKDeallocBlockExecutor.h b/Pods/Headers/Public/DKNightVersion/DKDeallocBlockExecutor.h deleted file mode 120000 index 7893cb1..0000000 --- a/Pods/Headers/Public/DKNightVersion/DKDeallocBlockExecutor.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/DeallocBlockExecutor/DKDeallocBlockExecutor.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/DKImage.h b/Pods/Headers/Public/DKNightVersion/DKImage.h deleted file mode 120000 index f815ac0..0000000 --- a/Pods/Headers/Public/DKNightVersion/DKImage.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Core/DKImage.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/DKNightVersion.h b/Pods/Headers/Public/DKNightVersion/DKNightVersion.h deleted file mode 120000 index 83411fb..0000000 --- a/Pods/Headers/Public/DKNightVersion/DKNightVersion.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/DKNightVersion.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/DKNightVersionManager.h b/Pods/Headers/Public/DKNightVersion/DKNightVersionManager.h deleted file mode 120000 index 6255e9d..0000000 --- a/Pods/Headers/Public/DKNightVersion/DKNightVersionManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Core/DKNightVersionManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/EXTKeyPathCoding.h b/Pods/Headers/Public/DKNightVersion/EXTKeyPathCoding.h deleted file mode 120000 index 10bedf6..0000000 --- a/Pods/Headers/Public/DKNightVersion/EXTKeyPathCoding.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/extobjc/EXTKeyPathCoding.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/NSObject+DeallocBlock.h b/Pods/Headers/Public/DKNightVersion/NSObject+DeallocBlock.h deleted file mode 120000 index a7bdc4d..0000000 --- a/Pods/Headers/Public/DKNightVersion/NSObject+DeallocBlock.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/DeallocBlockExecutor/NSObject+DeallocBlock.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/NSObject+Night.h b/Pods/Headers/Public/DKNightVersion/NSObject+Night.h deleted file mode 120000 index 34fe423..0000000 --- a/Pods/Headers/Public/DKNightVersion/NSObject+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Core/NSObject+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UIBarButtonItem+Night.h b/Pods/Headers/Public/DKNightVersion/UIBarButtonItem+Night.h deleted file mode 120000 index b00dcaf..0000000 --- a/Pods/Headers/Public/DKNightVersion/UIBarButtonItem+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIBarButtonItem+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UIButton+Night.h b/Pods/Headers/Public/DKNightVersion/UIButton+Night.h deleted file mode 120000 index 83e7442..0000000 --- a/Pods/Headers/Public/DKNightVersion/UIButton+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Manual/UIButton+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UIControl+Night.h b/Pods/Headers/Public/DKNightVersion/UIControl+Night.h deleted file mode 120000 index 229825e..0000000 --- a/Pods/Headers/Public/DKNightVersion/UIControl+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIControl+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UIImageView+Night.h b/Pods/Headers/Public/DKNightVersion/UIImageView+Night.h deleted file mode 120000 index 8605ee8..0000000 --- a/Pods/Headers/Public/DKNightVersion/UIImageView+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Manual/UIImageView+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UILabel+Night.h b/Pods/Headers/Public/DKNightVersion/UILabel+Night.h deleted file mode 120000 index bd8fddb..0000000 --- a/Pods/Headers/Public/DKNightVersion/UILabel+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UILabel+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UINavigationBar+Animation.h b/Pods/Headers/Public/DKNightVersion/UINavigationBar+Animation.h deleted file mode 120000 index ea11500..0000000 --- a/Pods/Headers/Public/DKNightVersion/UINavigationBar+Animation.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Manual/UINavigationBar+Animation.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UINavigationBar+Night.h b/Pods/Headers/Public/DKNightVersion/UINavigationBar+Night.h deleted file mode 120000 index b5e8787..0000000 --- a/Pods/Headers/Public/DKNightVersion/UINavigationBar+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UINavigationBar+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UIPageControl+Night.h b/Pods/Headers/Public/DKNightVersion/UIPageControl+Night.h deleted file mode 120000 index 890b0eb..0000000 --- a/Pods/Headers/Public/DKNightVersion/UIPageControl+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIPageControl+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UIProgressView+Night.h b/Pods/Headers/Public/DKNightVersion/UIProgressView+Night.h deleted file mode 120000 index 4fa22ef..0000000 --- a/Pods/Headers/Public/DKNightVersion/UIProgressView+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIProgressView+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UISearchBar+Night.h b/Pods/Headers/Public/DKNightVersion/UISearchBar+Night.h deleted file mode 120000 index b06aeee..0000000 --- a/Pods/Headers/Public/DKNightVersion/UISearchBar+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UISearchBar+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UISlider+Night.h b/Pods/Headers/Public/DKNightVersion/UISlider+Night.h deleted file mode 120000 index 336ae17..0000000 --- a/Pods/Headers/Public/DKNightVersion/UISlider+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UISlider+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UISwitch+Night.h b/Pods/Headers/Public/DKNightVersion/UISwitch+Night.h deleted file mode 120000 index 1951335..0000000 --- a/Pods/Headers/Public/DKNightVersion/UISwitch+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UISwitch+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UITabBar+Night.h b/Pods/Headers/Public/DKNightVersion/UITabBar+Night.h deleted file mode 120000 index 4a8cc5a..0000000 --- a/Pods/Headers/Public/DKNightVersion/UITabBar+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UITabBar+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UITableView+Night.h b/Pods/Headers/Public/DKNightVersion/UITableView+Night.h deleted file mode 120000 index 96f6423..0000000 --- a/Pods/Headers/Public/DKNightVersion/UITableView+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UITableView+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UITextField+Keyboard.h b/Pods/Headers/Public/DKNightVersion/UITextField+Keyboard.h deleted file mode 120000 index 39df51f..0000000 --- a/Pods/Headers/Public/DKNightVersion/UITextField+Keyboard.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/Manual/UITextField+Keyboard.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UITextField+Night.h b/Pods/Headers/Public/DKNightVersion/UITextField+Night.h deleted file mode 120000 index 7da49ae..0000000 --- a/Pods/Headers/Public/DKNightVersion/UITextField+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UITextField+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UITextView+Night.h b/Pods/Headers/Public/DKNightVersion/UITextView+Night.h deleted file mode 120000 index e2adda5..0000000 --- a/Pods/Headers/Public/DKNightVersion/UITextView+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UITextView+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UIToolbar+Night.h b/Pods/Headers/Public/DKNightVersion/UIToolbar+Night.h deleted file mode 120000 index 0a42d6e..0000000 --- a/Pods/Headers/Public/DKNightVersion/UIToolbar+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIToolbar+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/UIView+Night.h b/Pods/Headers/Public/DKNightVersion/UIView+Night.h deleted file mode 120000 index af0f2db..0000000 --- a/Pods/Headers/Public/DKNightVersion/UIView+Night.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/UIKit/UIView+Night.h \ No newline at end of file diff --git a/Pods/Headers/Public/DKNightVersion/metamacros.h b/Pods/Headers/Public/DKNightVersion/metamacros.h deleted file mode 120000 index e8fe88b..0000000 --- a/Pods/Headers/Public/DKNightVersion/metamacros.h +++ /dev/null @@ -1 +0,0 @@ -../../../DKNightVersion/DKNightVersion/extobjc/metamacros.h \ No newline at end of file diff --git a/Pods/Headers/Public/FMDB/FMDB.h b/Pods/Headers/Public/FMDB/FMDB.h deleted file mode 120000 index bcd6e0a..0000000 --- a/Pods/Headers/Public/FMDB/FMDB.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMDB.h \ No newline at end of file diff --git a/Pods/Headers/Public/FMDB/FMDatabase.h b/Pods/Headers/Public/FMDB/FMDatabase.h deleted file mode 120000 index e69b333..0000000 --- a/Pods/Headers/Public/FMDB/FMDatabase.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMDatabase.h \ No newline at end of file diff --git a/Pods/Headers/Public/FMDB/FMDatabaseAdditions.h b/Pods/Headers/Public/FMDB/FMDatabaseAdditions.h deleted file mode 120000 index b48a6a3..0000000 --- a/Pods/Headers/Public/FMDB/FMDatabaseAdditions.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMDatabaseAdditions.h \ No newline at end of file diff --git a/Pods/Headers/Public/FMDB/FMDatabasePool.h b/Pods/Headers/Public/FMDB/FMDatabasePool.h deleted file mode 120000 index 1d78001..0000000 --- a/Pods/Headers/Public/FMDB/FMDatabasePool.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMDatabasePool.h \ No newline at end of file diff --git a/Pods/Headers/Public/FMDB/FMDatabaseQueue.h b/Pods/Headers/Public/FMDB/FMDatabaseQueue.h deleted file mode 120000 index 9adde87..0000000 --- a/Pods/Headers/Public/FMDB/FMDatabaseQueue.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMDatabaseQueue.h \ No newline at end of file diff --git a/Pods/Headers/Public/FMDB/FMResultSet.h b/Pods/Headers/Public/FMDB/FMResultSet.h deleted file mode 120000 index fd761d8..0000000 --- a/Pods/Headers/Public/FMDB/FMResultSet.h +++ /dev/null @@ -1 +0,0 @@ -../../../FMDB/src/fmdb/FMResultSet.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJExtension.h b/Pods/Headers/Public/MJExtension/MJExtension.h deleted file mode 120000 index 1df1880..0000000 --- a/Pods/Headers/Public/MJExtension/MJExtension.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJExtensionConst.h b/Pods/Headers/Public/MJExtension/MJExtensionConst.h deleted file mode 120000 index f1a01cd..0000000 --- a/Pods/Headers/Public/MJExtension/MJExtensionConst.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJExtensionConst.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJFoundation.h b/Pods/Headers/Public/MJExtension/MJFoundation.h deleted file mode 120000 index 7c66d2f..0000000 --- a/Pods/Headers/Public/MJExtension/MJFoundation.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJFoundation.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJProperty.h b/Pods/Headers/Public/MJExtension/MJProperty.h deleted file mode 120000 index 5361eff..0000000 --- a/Pods/Headers/Public/MJExtension/MJProperty.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJProperty.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJPropertyKey.h b/Pods/Headers/Public/MJExtension/MJPropertyKey.h deleted file mode 120000 index d740b30..0000000 --- a/Pods/Headers/Public/MJExtension/MJPropertyKey.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJPropertyKey.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJPropertyType.h b/Pods/Headers/Public/MJExtension/MJPropertyType.h deleted file mode 120000 index 3ab7735..0000000 --- a/Pods/Headers/Public/MJExtension/MJPropertyType.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/MJPropertyType.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/NSObject+MJClass.h b/Pods/Headers/Public/MJExtension/NSObject+MJClass.h deleted file mode 120000 index 9dde422..0000000 --- a/Pods/Headers/Public/MJExtension/NSObject+MJClass.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/NSObject+MJClass.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/NSObject+MJCoding.h b/Pods/Headers/Public/MJExtension/NSObject+MJCoding.h deleted file mode 120000 index 8f4f799..0000000 --- a/Pods/Headers/Public/MJExtension/NSObject+MJCoding.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/NSObject+MJCoding.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/NSObject+MJKeyValue.h b/Pods/Headers/Public/MJExtension/NSObject+MJKeyValue.h deleted file mode 120000 index 9d5b17d..0000000 --- a/Pods/Headers/Public/MJExtension/NSObject+MJKeyValue.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/NSObject+MJKeyValue.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/NSObject+MJProperty.h b/Pods/Headers/Public/MJExtension/NSObject+MJProperty.h deleted file mode 120000 index 2ee634e..0000000 --- a/Pods/Headers/Public/MJExtension/NSObject+MJProperty.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/NSObject+MJProperty.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/NSString+MJExtension.h b/Pods/Headers/Public/MJExtension/NSString+MJExtension.h deleted file mode 120000 index 583ce69..0000000 --- a/Pods/Headers/Public/MJExtension/NSString+MJExtension.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJExtension/MJExtension/NSString+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefresh.h b/Pods/Headers/Public/MJRefresh/MJRefresh.h deleted file mode 120000 index d83fdd6..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefresh.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshAutoFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshAutoFooter.h deleted file mode 120000 index 5bb1fbc..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshAutoFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshAutoGifFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshAutoGifFooter.h deleted file mode 120000 index 1435437..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshAutoGifFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshAutoNormalFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshAutoNormalFooter.h deleted file mode 120000 index 6b87abe..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshAutoNormalFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshAutoStateFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshAutoStateFooter.h deleted file mode 120000 index 19b2053..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshAutoStateFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshBackFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshBackFooter.h deleted file mode 120000 index 3df4973..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshBackFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshBackGifFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshBackGifFooter.h deleted file mode 120000 index e5d5f96..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshBackGifFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshBackNormalFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshBackNormalFooter.h deleted file mode 120000 index c578c7f..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshBackNormalFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshBackStateFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshBackStateFooter.h deleted file mode 120000 index 3317188..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshBackStateFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshComponent.h b/Pods/Headers/Public/MJRefresh/MJRefreshComponent.h deleted file mode 120000 index 35cb1b1..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshComponent.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Base/MJRefreshComponent.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshConst.h b/Pods/Headers/Public/MJRefresh/MJRefreshConst.h deleted file mode 120000 index 035954b..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshConst.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/MJRefreshConst.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshFooter.h deleted file mode 120000 index 743dda2..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshFooter.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Base/MJRefreshFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshGifHeader.h b/Pods/Headers/Public/MJRefresh/MJRefreshGifHeader.h deleted file mode 120000 index aa6afbd..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshGifHeader.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshHeader.h b/Pods/Headers/Public/MJRefresh/MJRefreshHeader.h deleted file mode 120000 index 3a38b26..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshHeader.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Base/MJRefreshHeader.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshNormalHeader.h b/Pods/Headers/Public/MJRefresh/MJRefreshNormalHeader.h deleted file mode 120000 index b5cc164..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshNormalHeader.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshStateHeader.h b/Pods/Headers/Public/MJRefresh/MJRefreshStateHeader.h deleted file mode 120000 index 7bd25cf..0000000 --- a/Pods/Headers/Public/MJRefresh/MJRefreshStateHeader.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/UIScrollView+MJExtension.h b/Pods/Headers/Public/MJRefresh/UIScrollView+MJExtension.h deleted file mode 120000 index 1919784..0000000 --- a/Pods/Headers/Public/MJRefresh/UIScrollView+MJExtension.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/UIScrollView+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/UIScrollView+MJRefresh.h b/Pods/Headers/Public/MJRefresh/UIScrollView+MJRefresh.h deleted file mode 120000 index 47b703d..0000000 --- a/Pods/Headers/Public/MJRefresh/UIScrollView+MJRefresh.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/UIScrollView+MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/UIView+MJExtension.h b/Pods/Headers/Public/MJRefresh/UIView+MJExtension.h deleted file mode 120000 index 5f19813..0000000 --- a/Pods/Headers/Public/MJRefresh/UIView+MJExtension.h +++ /dev/null @@ -1 +0,0 @@ -../../../MJRefresh/MJRefresh/UIView+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Public/NJKWebViewProgress/NJKWebViewProgress.h b/Pods/Headers/Public/NJKWebViewProgress/NJKWebViewProgress.h deleted file mode 120000 index 532f6eb..0000000 --- a/Pods/Headers/Public/NJKWebViewProgress/NJKWebViewProgress.h +++ /dev/null @@ -1 +0,0 @@ -../../../NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgress.h \ No newline at end of file diff --git a/Pods/Headers/Public/NJKWebViewProgress/NJKWebViewProgressView.h b/Pods/Headers/Public/NJKWebViewProgress/NJKWebViewProgressView.h deleted file mode 120000 index 58af127..0000000 --- a/Pods/Headers/Public/NJKWebViewProgress/NJKWebViewProgressView.h +++ /dev/null @@ -1 +0,0 @@ -../../../NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgressView.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/NSData+ImageContentType.h b/Pods/Headers/Public/SDWebImage/NSData+ImageContentType.h deleted file mode 120000 index 8457498..0000000 --- a/Pods/Headers/Public/SDWebImage/NSData+ImageContentType.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/NSData+ImageContentType.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/SDImageCache.h b/Pods/Headers/Public/SDWebImage/SDImageCache.h deleted file mode 120000 index 0040b06..0000000 --- a/Pods/Headers/Public/SDWebImage/SDImageCache.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDImageCache.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/SDWebImageCompat.h b/Pods/Headers/Public/SDWebImage/SDWebImageCompat.h deleted file mode 120000 index 6ca2478..0000000 --- a/Pods/Headers/Public/SDWebImage/SDWebImageCompat.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageCompat.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/SDWebImageDecoder.h b/Pods/Headers/Public/SDWebImage/SDWebImageDecoder.h deleted file mode 120000 index a2f3a68..0000000 --- a/Pods/Headers/Public/SDWebImage/SDWebImageDecoder.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageDecoder.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/SDWebImageDownloader.h b/Pods/Headers/Public/SDWebImage/SDWebImageDownloader.h deleted file mode 120000 index 303b03b..0000000 --- a/Pods/Headers/Public/SDWebImage/SDWebImageDownloader.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageDownloader.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/SDWebImageDownloaderOperation.h b/Pods/Headers/Public/SDWebImage/SDWebImageDownloaderOperation.h deleted file mode 120000 index 99441c4..0000000 --- a/Pods/Headers/Public/SDWebImage/SDWebImageDownloaderOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/SDWebImageManager.h b/Pods/Headers/Public/SDWebImage/SDWebImageManager.h deleted file mode 120000 index 1b81848..0000000 --- a/Pods/Headers/Public/SDWebImage/SDWebImageManager.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/SDWebImageOperation.h b/Pods/Headers/Public/SDWebImage/SDWebImageOperation.h deleted file mode 120000 index 20e5b89..0000000 --- a/Pods/Headers/Public/SDWebImage/SDWebImageOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImageOperation.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/SDWebImagePrefetcher.h b/Pods/Headers/Public/SDWebImage/SDWebImagePrefetcher.h deleted file mode 120000 index 50585c6..0000000 --- a/Pods/Headers/Public/SDWebImage/SDWebImagePrefetcher.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/SDWebImagePrefetcher.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/UIButton+WebCache.h b/Pods/Headers/Public/SDWebImage/UIButton+WebCache.h deleted file mode 120000 index 19d2d8e..0000000 --- a/Pods/Headers/Public/SDWebImage/UIButton+WebCache.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIButton+WebCache.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/UIImage+GIF.h b/Pods/Headers/Public/SDWebImage/UIImage+GIF.h deleted file mode 120000 index 14d5aad..0000000 --- a/Pods/Headers/Public/SDWebImage/UIImage+GIF.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIImage+GIF.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/UIImage+MultiFormat.h b/Pods/Headers/Public/SDWebImage/UIImage+MultiFormat.h deleted file mode 120000 index 1fb9650..0000000 --- a/Pods/Headers/Public/SDWebImage/UIImage+MultiFormat.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIImage+MultiFormat.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/UIImageView+HighlightedWebCache.h b/Pods/Headers/Public/SDWebImage/UIImageView+HighlightedWebCache.h deleted file mode 120000 index fd4dea4..0000000 --- a/Pods/Headers/Public/SDWebImage/UIImageView+HighlightedWebCache.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/UIImageView+WebCache.h b/Pods/Headers/Public/SDWebImage/UIImageView+WebCache.h deleted file mode 120000 index 0c53a47..0000000 --- a/Pods/Headers/Public/SDWebImage/UIImageView+WebCache.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIImageView+WebCache.h \ No newline at end of file diff --git a/Pods/Headers/Public/SDWebImage/UIView+WebCacheOperation.h b/Pods/Headers/Public/SDWebImage/UIView+WebCacheOperation.h deleted file mode 120000 index f9890c4..0000000 --- a/Pods/Headers/Public/SDWebImage/UIView+WebCacheOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../SDWebImage/SDWebImage/UIView+WebCacheOperation.h \ No newline at end of file diff --git a/Pods/Headers/Public/SVProgressHUD/SVIndefiniteAnimatedView.h b/Pods/Headers/Public/SVProgressHUD/SVIndefiniteAnimatedView.h deleted file mode 120000 index 55a38a2..0000000 --- a/Pods/Headers/Public/SVProgressHUD/SVIndefiniteAnimatedView.h +++ /dev/null @@ -1 +0,0 @@ -../../../SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.h \ No newline at end of file diff --git a/Pods/Headers/Public/SVProgressHUD/SVProgressAnimatedView.h b/Pods/Headers/Public/SVProgressHUD/SVProgressAnimatedView.h deleted file mode 120000 index 581cdc2..0000000 --- a/Pods/Headers/Public/SVProgressHUD/SVProgressAnimatedView.h +++ /dev/null @@ -1 +0,0 @@ -../../../SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.h \ No newline at end of file diff --git a/Pods/Headers/Public/SVProgressHUD/SVProgressHUD.h b/Pods/Headers/Public/SVProgressHUD/SVProgressHUD.h deleted file mode 120000 index 608a8aa..0000000 --- a/Pods/Headers/Public/SVProgressHUD/SVProgressHUD.h +++ /dev/null @@ -1 +0,0 @@ -../../../SVProgressHUD/SVProgressHUD/SVProgressHUD.h \ No newline at end of file diff --git a/Pods/Headers/Public/SVProgressHUD/SVRadialGradientLayer.h b/Pods/Headers/Public/SVProgressHUD/SVRadialGradientLayer.h deleted file mode 120000 index d78beb5..0000000 --- a/Pods/Headers/Public/SVProgressHUD/SVRadialGradientLayer.h +++ /dev/null @@ -1 +0,0 @@ -../../../SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POP.h b/Pods/Headers/Public/pop/POP.h deleted file mode 120000 index dd15660..0000000 --- a/Pods/Headers/Public/pop/POP.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POP.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPAnimatableProperty.h b/Pods/Headers/Public/pop/POPAnimatableProperty.h deleted file mode 120000 index 48fd8c4..0000000 --- a/Pods/Headers/Public/pop/POPAnimatableProperty.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimatableProperty.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPAnimation.h b/Pods/Headers/Public/pop/POPAnimation.h deleted file mode 120000 index dfe8a85..0000000 --- a/Pods/Headers/Public/pop/POPAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPAnimationEvent.h b/Pods/Headers/Public/pop/POPAnimationEvent.h deleted file mode 120000 index 5d40492..0000000 --- a/Pods/Headers/Public/pop/POPAnimationEvent.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationEvent.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPAnimationExtras.h b/Pods/Headers/Public/pop/POPAnimationExtras.h deleted file mode 120000 index 3a1bc7e..0000000 --- a/Pods/Headers/Public/pop/POPAnimationExtras.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationExtras.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPAnimationTracer.h b/Pods/Headers/Public/pop/POPAnimationTracer.h deleted file mode 120000 index 64fff66..0000000 --- a/Pods/Headers/Public/pop/POPAnimationTracer.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimationTracer.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPAnimator.h b/Pods/Headers/Public/pop/POPAnimator.h deleted file mode 120000 index 89707ea..0000000 --- a/Pods/Headers/Public/pop/POPAnimator.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPAnimator.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPBasicAnimation.h b/Pods/Headers/Public/pop/POPBasicAnimation.h deleted file mode 120000 index 50184f2..0000000 --- a/Pods/Headers/Public/pop/POPBasicAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPBasicAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPCustomAnimation.h b/Pods/Headers/Public/pop/POPCustomAnimation.h deleted file mode 120000 index 5025bd6..0000000 --- a/Pods/Headers/Public/pop/POPCustomAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPCustomAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPDecayAnimation.h b/Pods/Headers/Public/pop/POPDecayAnimation.h deleted file mode 120000 index dba2796..0000000 --- a/Pods/Headers/Public/pop/POPDecayAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPDecayAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPDefines.h b/Pods/Headers/Public/pop/POPDefines.h deleted file mode 120000 index 4b10036..0000000 --- a/Pods/Headers/Public/pop/POPDefines.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPDefines.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPGeometry.h b/Pods/Headers/Public/pop/POPGeometry.h deleted file mode 120000 index 9b29189..0000000 --- a/Pods/Headers/Public/pop/POPGeometry.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPGeometry.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPLayerExtras.h b/Pods/Headers/Public/pop/POPLayerExtras.h deleted file mode 120000 index e11f4c6..0000000 --- a/Pods/Headers/Public/pop/POPLayerExtras.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPLayerExtras.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPPropertyAnimation.h b/Pods/Headers/Public/pop/POPPropertyAnimation.h deleted file mode 120000 index 0fae4c5..0000000 --- a/Pods/Headers/Public/pop/POPPropertyAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPPropertyAnimation.h \ No newline at end of file diff --git a/Pods/Headers/Public/pop/POPSpringAnimation.h b/Pods/Headers/Public/pop/POPSpringAnimation.h deleted file mode 120000 index 152f663..0000000 --- a/Pods/Headers/Public/pop/POPSpringAnimation.h +++ /dev/null @@ -1 +0,0 @@ -../../../pop/pop/POPSpringAnimation.h \ No newline at end of file diff --git a/Pods/MJExtension/LICENSE b/Pods/MJExtension/LICENSE deleted file mode 100644 index c502f7f..0000000 --- a/Pods/MJExtension/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2013-2015 MJExtension (https://github.com/CoderMJLee/MJExtension) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/Pods/MJExtension/MJExtension/MJExtension.h b/Pods/MJExtension/MJExtension/MJExtension.h deleted file mode 100755 index ad35d09..0000000 --- a/Pods/MJExtension/MJExtension/MJExtension.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// MJExtension.h -// MJExtension -// -// Created by mj on 14-1-15. -// Copyright (c) 2014年 小码哥. All rights reserved. -// 代码地址:https://github.com/CoderMJLee/MJExtension -// 代码地址:http://code4app.com/ios/%E5%AD%97%E5%85%B8-JSON-%E4%B8%8E%E6%A8%A1%E5%9E%8B%E7%9A%84%E8%BD%AC%E6%8D%A2/5339992a933bf062608b4c57 - -#import "NSObject+MJCoding.h" -#import "NSObject+MJProperty.h" -#import "NSObject+MJClass.h" -#import "NSObject+MJKeyValue.h" -#import "NSString+MJExtension.h" -#import "MJExtensionConst.h" \ No newline at end of file diff --git a/Pods/MJExtension/MJExtension/MJExtensionConst.h b/Pods/MJExtension/MJExtension/MJExtensionConst.h deleted file mode 100644 index ba8893a..0000000 --- a/Pods/MJExtension/MJExtension/MJExtensionConst.h +++ /dev/null @@ -1,88 +0,0 @@ - -#ifndef __MJExtensionConst__H__ -#define __MJExtensionConst__H__ - -#import - -// 过期 -#define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) - -// 构建错误 -#define MJExtensionBuildError(clazz, msg) \ -NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \ -[clazz setMj_error:error]; - -// 日志输出 -#ifdef DEBUG -#define MJExtensionLog(...) NSLog(__VA_ARGS__) -#else -#define MJExtensionLog(...) -#endif - -/** - * 断言 - * @param condition 条件 - * @param returnValue 返回值 - */ -#define MJExtensionAssertError(condition, returnValue, clazz, msg) \ -[clazz setMj_error:nil]; \ -if ((condition) == NO) { \ - MJExtensionBuildError(clazz, msg); \ - return returnValue;\ -} - -#define MJExtensionAssert2(condition, returnValue) \ -if ((condition) == NO) return returnValue; - -/** - * 断言 - * @param condition 条件 - */ -#define MJExtensionAssert(condition) MJExtensionAssert2(condition, ) - -/** - * 断言 - * @param param 参数 - * @param returnValue 返回值 - */ -#define MJExtensionAssertParamNotNil2(param, returnValue) \ -MJExtensionAssert2((param) != nil, returnValue) - -/** - * 断言 - * @param param 参数 - */ -#define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, ) - -/** - * 打印所有的属性 - */ -#define MJLogAllIvars \ --(NSString *)description \ -{ \ - return [self mj_keyValues].description; \ -} -#define MJExtensionLogAllProperties MJLogAllIvars - -/** - * 类型(属性类型) - */ -extern NSString *const MJPropertyTypeInt; -extern NSString *const MJPropertyTypeShort; -extern NSString *const MJPropertyTypeFloat; -extern NSString *const MJPropertyTypeDouble; -extern NSString *const MJPropertyTypeLong; -extern NSString *const MJPropertyTypeLongLong; -extern NSString *const MJPropertyTypeChar; -extern NSString *const MJPropertyTypeBOOL1; -extern NSString *const MJPropertyTypeBOOL2; -extern NSString *const MJPropertyTypePointer; - -extern NSString *const MJPropertyTypeIvar; -extern NSString *const MJPropertyTypeMethod; -extern NSString *const MJPropertyTypeBlock; -extern NSString *const MJPropertyTypeClass; -extern NSString *const MJPropertyTypeSEL; -extern NSString *const MJPropertyTypeId; - -#endif \ No newline at end of file diff --git a/Pods/MJExtension/MJExtension/MJExtensionConst.m b/Pods/MJExtension/MJExtension/MJExtensionConst.m deleted file mode 100644 index 24bcca5..0000000 --- a/Pods/MJExtension/MJExtension/MJExtensionConst.m +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef __MJExtensionConst__M__ -#define __MJExtensionConst__M__ - -#import - -/** - * 成员变量类型(属性类型) - */ -NSString *const MJPropertyTypeInt = @"i"; -NSString *const MJPropertyTypeShort = @"s"; -NSString *const MJPropertyTypeFloat = @"f"; -NSString *const MJPropertyTypeDouble = @"d"; -NSString *const MJPropertyTypeLong = @"l"; -NSString *const MJPropertyTypeLongLong = @"q"; -NSString *const MJPropertyTypeChar = @"c"; -NSString *const MJPropertyTypeBOOL1 = @"c"; -NSString *const MJPropertyTypeBOOL2 = @"b"; -NSString *const MJPropertyTypePointer = @"*"; - -NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}"; -NSString *const MJPropertyTypeMethod = @"^{objc_method=}"; -NSString *const MJPropertyTypeBlock = @"@?"; -NSString *const MJPropertyTypeClass = @"#"; -NSString *const MJPropertyTypeSEL = @":"; -NSString *const MJPropertyTypeId = @"@"; - -#endif \ No newline at end of file diff --git a/Pods/MJExtension/MJExtension/MJFoundation.h b/Pods/MJExtension/MJExtension/MJFoundation.h deleted file mode 100644 index 8a0e457..0000000 --- a/Pods/MJExtension/MJExtension/MJFoundation.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// MJFoundation.h -// MJExtensionExample -// -// Created by MJ Lee on 14/7/16. -// Copyright (c) 2014年 小码哥. All rights reserved. -// - -#import - -@interface MJFoundation : NSObject -+ (BOOL)isClassFromFoundation:(Class)c; -@end diff --git a/Pods/MJExtension/MJExtension/MJFoundation.m b/Pods/MJExtension/MJExtension/MJFoundation.m deleted file mode 100644 index 23a7d8c..0000000 --- a/Pods/MJExtension/MJExtension/MJFoundation.m +++ /dev/null @@ -1,48 +0,0 @@ -// -// MJFoundation.m -// MJExtensionExample -// -// Created by MJ Lee on 14/7/16. -// Copyright (c) 2014年 小码哥. All rights reserved. -// - -#import "MJFoundation.h" -#import "MJExtensionConst.h" -#import - -static NSSet *foundationClasses_; - -@implementation MJFoundation - -+ (NSSet *)foundationClasses -{ - if (foundationClasses_ == nil) { - // 集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断 - foundationClasses_ = [NSSet setWithObjects: - [NSURL class], - [NSDate class], - [NSValue class], - [NSData class], - [NSError class], - [NSArray class], - [NSDictionary class], - [NSString class], - [NSAttributedString class], nil]; - } - return foundationClasses_; -} - -+ (BOOL)isClassFromFoundation:(Class)c -{ - if (c == [NSObject class] || c == [NSManagedObject class]) return YES; - - __block BOOL result = NO; - [[self foundationClasses] enumerateObjectsUsingBlock:^(Class foundationClass, BOOL *stop) { - if ([c isSubclassOfClass:foundationClass]) { - result = YES; - *stop = YES; - } - }]; - return result; -} -@end diff --git a/Pods/MJExtension/MJExtension/MJProperty.h b/Pods/MJExtension/MJExtension/MJProperty.h deleted file mode 100644 index 90ac6bc..0000000 --- a/Pods/MJExtension/MJExtension/MJProperty.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// MJProperty.h -// MJExtensionExample -// -// Created by MJ Lee on 15/4/17. -// Copyright (c) 2015年 小码哥. All rights reserved. -// 包装一个成员属性 - -#import -#import -#import "MJPropertyType.h" -#import "MJPropertyKey.h" - -/** - * 包装一个成员 - */ -@interface MJProperty : NSObject -/** 成员属性 */ -@property (nonatomic, assign) objc_property_t property; -/** 成员属性的名字 */ -@property (nonatomic, readonly) NSString *name; - -/** 成员属性的类型 */ -@property (nonatomic, readonly) MJPropertyType *type; -/** 成员属性来源于哪个类(可能是父类) */ -@property (nonatomic, assign) Class srcClass; - -/**** 同一个成员属性 - 父类和子类的行为可能不一致(originKey、propertyKeys、objectClassInArray) ****/ -/** 设置最原始的key */ -- (void)setOriginKey:(id)originKey forClass:(Class)c; -/** 对应着字典中的多级key(里面存放的数组,数组里面都是MJPropertyKey对象) */ -- (NSArray *)propertyKeysForClass:(Class)c; - -/** 模型数组中的模型类型 */ -- (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c; -- (Class)objectClassInArrayForClass:(Class)c; -/**** 同一个成员变量 - 父类和子类的行为可能不一致(key、keys、objectClassInArray) ****/ - -/** - * 设置object的成员变量值 - */ -- (void)setValue:(id)value forObject:(id)object; -/** - * 得到object的成员属性值 - */ -- (id)valueForObject:(id)object; - -/** - * 初始化 - */ -+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property; - -@end diff --git a/Pods/MJExtension/MJExtension/MJProperty.m b/Pods/MJExtension/MJExtension/MJProperty.m deleted file mode 100644 index 2489d9a..0000000 --- a/Pods/MJExtension/MJExtension/MJProperty.m +++ /dev/null @@ -1,177 +0,0 @@ -// -// MJProperty.m -// MJExtensionExample -// -// Created by MJ Lee on 15/4/17. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJProperty.h" -#import "MJFoundation.h" -#import "MJExtensionConst.h" -#import - -@interface MJProperty() -@property (strong, nonatomic) NSMutableDictionary *propertyKeysDict; -@property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict; -@end - -@implementation MJProperty - -#pragma mark - 懒加载 -- (NSMutableDictionary *)propertyKeysDict -{ - if (!_propertyKeysDict) { - _propertyKeysDict = [NSMutableDictionary dictionary]; - } - return _propertyKeysDict; -} - -- (NSMutableDictionary *)objectClassInArrayDict -{ - if (!_objectClassInArrayDict) { - _objectClassInArrayDict = [NSMutableDictionary dictionary]; - } - return _objectClassInArrayDict; -} - -#pragma mark - 缓存 -+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property -{ - MJProperty *propertyObj = objc_getAssociatedObject(self, property); - if (propertyObj == nil) { - propertyObj = [[self alloc] init]; - propertyObj.property = property; - objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - return propertyObj; -} - -#pragma mark - 公共方法 -- (void)setProperty:(objc_property_t)property -{ - _property = property; - - MJExtensionAssertParamNotNil(property); - - // 1.属性名 - _name = @(property_getName(property)); - - // 2.成员类型 - NSString *attrs = @(property_getAttributes(property)); - NSUInteger dotLoc = [attrs rangeOfString:@","].location; - NSString *code = nil; - NSUInteger loc = 1; - if (dotLoc == NSNotFound) { // 没有, - code = [attrs substringFromIndex:loc]; - } else { - code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)]; - } - _type = [MJPropertyType cachedTypeWithCode:code]; -} - -/** - * 获得成员变量的值 - */ -- (id)valueForObject:(id)object -{ - if (self.type.KVCDisabled) return [NSNull null]; - return [object valueForKey:self.name]; -} - -/** - * 设置成员变量的值 - */ -- (void)setValue:(id)value forObject:(id)object -{ - if (self.type.KVCDisabled || value == nil) return; - [object setValue:value forKey:self.name]; -} - -/** - * 通过字符串key创建对应的keys - */ -- (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey -{ - if (stringKey.length == 0) return nil; - - NSMutableArray *propertyKeys = [NSMutableArray array]; - // 如果有多级映射 - NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."]; - - for (NSString *oldKey in oldKeys) { - NSUInteger start = [oldKey rangeOfString:@"["].location; - if (start != NSNotFound) { // 有索引的key - NSString *prefixKey = [oldKey substringToIndex:start]; - NSString *indexKey = prefixKey; - if (prefixKey.length) { - MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init]; - propertyKey.name = prefixKey; - [propertyKeys addObject:propertyKey]; - - indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""]; - } - - /** 解析索引 **/ - // 元素 - NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"]; - for (NSInteger i = 0; i - -typedef enum { - MJPropertyKeyTypeDictionary = 0, // 字典的key - MJPropertyKeyTypeArray // 数组的key -} MJPropertyKeyType; - -/** - * 属性的key - */ -@interface MJPropertyKey : NSObject -/** key的名字 */ -@property (copy, nonatomic) NSString *name; -/** key的种类,可能是@"10",可能是@"age" */ -@property (assign, nonatomic) MJPropertyKeyType type; - -/** - * 根据当前的key,也就是name,从object(字典或者数组)中取值 - */ -- (id)valueInObject:(id)object; - -@end diff --git a/Pods/MJExtension/MJExtension/MJPropertyKey.m b/Pods/MJExtension/MJExtension/MJPropertyKey.m deleted file mode 100644 index 438d019..0000000 --- a/Pods/MJExtension/MJExtension/MJPropertyKey.m +++ /dev/null @@ -1,25 +0,0 @@ -// -// MJPropertyKey.m -// MJExtensionExample -// -// Created by MJ Lee on 15/8/11. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJPropertyKey.h" - -@implementation MJPropertyKey - -- (id)valueInObject:(id)object -{ - if ([object isKindOfClass:[NSDictionary class]] && self.type == MJPropertyKeyTypeDictionary) { - return object[self.name]; - } else if ([object isKindOfClass:[NSArray class]] && self.type == MJPropertyKeyTypeArray) { - NSArray *array = object; - NSUInteger index = self.name.intValue; - if (index < array.count) return array[index]; - return nil; - } - return nil; -} -@end diff --git a/Pods/MJExtension/MJExtension/MJPropertyType.h b/Pods/MJExtension/MJExtension/MJPropertyType.h deleted file mode 100755 index 8c53f27..0000000 --- a/Pods/MJExtension/MJExtension/MJPropertyType.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// MJPropertyType.h -// MJExtension -// -// Created by mj on 14-1-15. -// Copyright (c) 2014年 小码哥. All rights reserved. -// 包装一种类型 - -#import - -/** - * 包装一种类型 - */ -@interface MJPropertyType : NSObject -/** 类型标识符 */ -@property (nonatomic, copy) NSString *code; - -/** 是否为id类型 */ -@property (nonatomic, readonly, getter=isIdType) BOOL idType; - -/** 是否为基本数字类型:int、float等 */ -@property (nonatomic, readonly, getter=isNumberType) BOOL numberType; - -/** 是否为BOOL类型 */ -@property (nonatomic, readonly, getter=isBoolType) BOOL boolType; - -/** 对象类型(如果是基本数据类型,此值为nil) */ -@property (nonatomic, readonly) Class typeClass; - -/** 类型是否来自于Foundation框架,比如NSString、NSArray */ -@property (nonatomic, readonly, getter = isFromFoundation) BOOL fromFoundation; -/** 类型是否不支持KVC */ -@property (nonatomic, readonly, getter = isKVCDisabled) BOOL KVCDisabled; - -/** - * 获得缓存的类型对象 - */ -+ (instancetype)cachedTypeWithCode:(NSString *)code; -@end \ No newline at end of file diff --git a/Pods/MJExtension/MJExtension/MJPropertyType.m b/Pods/MJExtension/MJExtension/MJPropertyType.m deleted file mode 100755 index b78b202..0000000 --- a/Pods/MJExtension/MJExtension/MJPropertyType.m +++ /dev/null @@ -1,71 +0,0 @@ -// -// MJPropertyType.m -// MJExtension -// -// Created by mj on 14-1-15. -// Copyright (c) 2014年 小码哥. All rights reserved. -// - -#import "MJPropertyType.h" -#import "MJExtension.h" -#import "MJFoundation.h" -#import "MJExtensionConst.h" - -@implementation MJPropertyType - -static NSMutableDictionary *types_; -+ (void)initialize -{ - types_ = [NSMutableDictionary dictionary]; -} - -+ (instancetype)cachedTypeWithCode:(NSString *)code -{ - MJExtensionAssertParamNotNil2(code, nil); - - MJPropertyType *type = types_[code]; - if (type == nil) { - type = [[self alloc] init]; - type.code = code; - types_[code] = type; - } - return type; -} - -#pragma mark - 公共方法 -- (void)setCode:(NSString *)code -{ - _code = code; - - MJExtensionAssertParamNotNil(code); - - if ([code isEqualToString:MJPropertyTypeId]) { - _idType = YES; - } else if (code.length == 0) { - _KVCDisabled = YES; - } else if (code.length > 3 && [code hasPrefix:@"@\""]) { - // 去掉@"和",截取中间的类型名称 - _code = [code substringWithRange:NSMakeRange(2, code.length - 3)]; - _typeClass = NSClassFromString(_code); - _fromFoundation = [MJFoundation isClassFromFoundation:_typeClass]; - _numberType = [_typeClass isSubclassOfClass:[NSNumber class]]; - - } else if ([code isEqualToString:MJPropertyTypeSEL] || - [code isEqualToString:MJPropertyTypeIvar] || - [code isEqualToString:MJPropertyTypeMethod]) { - _KVCDisabled = YES; - } - - // 是否为数字类型 - NSString *lowerCode = _code.lowercaseString; - NSArray *numberTypes = @[MJPropertyTypeInt, MJPropertyTypeShort, MJPropertyTypeBOOL1, MJPropertyTypeBOOL2, MJPropertyTypeFloat, MJPropertyTypeDouble, MJPropertyTypeLong, MJPropertyTypeLongLong, MJPropertyTypeChar]; - if ([numberTypes containsObject:lowerCode]) { - _numberType = YES; - - if ([lowerCode isEqualToString:MJPropertyTypeBOOL1] - || [lowerCode isEqualToString:MJPropertyTypeBOOL2]) { - _boolType = YES; - } - } -} -@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJClass.h b/Pods/MJExtension/MJExtension/NSObject+MJClass.h deleted file mode 100644 index e4ac1c5..0000000 --- a/Pods/MJExtension/MJExtension/NSObject+MJClass.h +++ /dev/null @@ -1,90 +0,0 @@ -// -// NSObject+MJClass.h -// MJExtensionExample -// -// Created by MJ Lee on 15/8/11. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import - -/** - * 遍历所有类的block(父类) - */ -typedef void (^MJClassesEnumeration)(Class c, BOOL *stop); - -/** 这个数组中的属性名才会进行字典和模型的转换 */ -typedef NSArray * (^MJAllowedPropertyNames)(); -/** 这个数组中的属性名才会进行归档 */ -typedef NSArray * (^MJAllowedCodingPropertyNames)(); - -/** 这个数组中的属性名将会被忽略:不进行字典和模型的转换 */ -typedef NSArray * (^MJIgnoredPropertyNames)(); -/** 这个数组中的属性名将会被忽略:不进行归档 */ -typedef NSArray * (^MJIgnoredCodingPropertyNames)(); - -/** - * 类相关的扩展 - */ -@interface NSObject (MJClass) -/** - * 遍历所有的类 - */ -+ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration; -+ (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration; - -#pragma mark - 属性白名单配置 -/** - * 这个数组中的属性名才会进行字典和模型的转换 - * - * @param allowedPropertyNames 这个数组中的属性名才会进行字典和模型的转换 - */ -+ (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; - -/** - * 这个数组中的属性名才会进行字典和模型的转换 - */ -+ (NSMutableArray *)mj_totalAllowedPropertyNames; - -#pragma mark - 属性黑名单配置 -/** - * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 - * - * @param ignoredPropertyNames 这个数组中的属性名将会被忽略:不进行字典和模型的转换 - */ -+ (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames; - -/** - * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 - */ -+ (NSMutableArray *)mj_totalIgnoredPropertyNames; - -#pragma mark - 归档属性白名单配置 -/** - * 这个数组中的属性名才会进行归档 - * - * @param allowedCodingPropertyNames 这个数组中的属性名才会进行归档 - */ -+ (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames; - -/** - * 这个数组中的属性名才会进行字典和模型的转换 - */ -+ (NSMutableArray *)mj_totalAllowedCodingPropertyNames; - -#pragma mark - 归档属性黑名单配置 -/** - * 这个数组中的属性名将会被忽略:不进行归档 - * - * @param ignoredCodingPropertyNames 这个数组中的属性名将会被忽略:不进行归档 - */ -+ (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames; - -/** - * 这个数组中的属性名将会被忽略:不进行归档 - */ -+ (NSMutableArray *)mj_totalIgnoredCodingPropertyNames; - -#pragma mark - 内部使用 -+ (void)mj_setupBlockReturnValue:(id (^)())block key:(const char *)key; -@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJClass.m b/Pods/MJExtension/MJExtension/NSObject+MJClass.m deleted file mode 100644 index 789b9a8..0000000 --- a/Pods/MJExtension/MJExtension/NSObject+MJClass.m +++ /dev/null @@ -1,168 +0,0 @@ -// -// NSObject+MJClass.m -// MJExtensionExample -// -// Created by MJ Lee on 15/8/11. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "NSObject+MJClass.h" -#import "NSObject+MJCoding.h" -#import "NSObject+MJKeyValue.h" -#import "MJFoundation.h" -#import - -static const char MJAllowedPropertyNamesKey = '\0'; -static const char MJIgnoredPropertyNamesKey = '\0'; -static const char MJAllowedCodingPropertyNamesKey = '\0'; -static const char MJIgnoredCodingPropertyNamesKey = '\0'; - -static NSMutableDictionary *allowedPropertyNamesDict_; -static NSMutableDictionary *ignoredPropertyNamesDict_; -static NSMutableDictionary *allowedCodingPropertyNamesDict_; -static NSMutableDictionary *ignoredCodingPropertyNamesDict_; - -@implementation NSObject (MJClass) - -+ (void)load -{ - allowedPropertyNamesDict_ = [NSMutableDictionary dictionary]; - ignoredPropertyNamesDict_ = [NSMutableDictionary dictionary]; - allowedCodingPropertyNamesDict_ = [NSMutableDictionary dictionary]; - ignoredCodingPropertyNamesDict_ = [NSMutableDictionary dictionary]; -} - -+ (NSMutableDictionary *)dictForKey:(const void *)key -{ - if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict_; - if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict_; - if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict_; - if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict_; - return nil; -} - -+ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration -{ - // 1.没有block就直接返回 - if (enumeration == nil) return; - - // 2.停止遍历的标记 - BOOL stop = NO; - - // 3.当前正在遍历的类 - Class c = self; - - // 4.开始遍历每一个类 - while (c && !stop) { - // 4.1.执行操作 - enumeration(c, &stop); - - // 4.2.获得父类 - c = class_getSuperclass(c); - - if ([MJFoundation isClassFromFoundation:c]) break; - } -} - -+ (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration -{ - // 1.没有block就直接返回 - if (enumeration == nil) return; - - // 2.停止遍历的标记 - BOOL stop = NO; - - // 3.当前正在遍历的类 - Class c = self; - - // 4.开始遍历每一个类 - while (c && !stop) { - // 4.1.执行操作 - enumeration(c, &stop); - - // 4.2.获得父类 - c = class_getSuperclass(c); - } -} - -#pragma mark - 属性黑名单配置 -+ (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames -{ - [self mj_setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey]; -} - -+ (NSMutableArray *)mj_totalIgnoredPropertyNames -{ - return [self mj_totalObjectsWithSelector:@selector(mj_ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey]; -} - -#pragma mark - 归档属性黑名单配置 -+ (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames -{ - [self mj_setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey]; -} - -+ (NSMutableArray *)mj_totalIgnoredCodingPropertyNames -{ - return [self mj_totalObjectsWithSelector:@selector(mj_ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey]; -} - -#pragma mark - 属性白名单配置 -+ (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; -{ - [self mj_setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey]; -} - -+ (NSMutableArray *)mj_totalAllowedPropertyNames -{ - return [self mj_totalObjectsWithSelector:@selector(mj_allowedPropertyNames) key:&MJAllowedPropertyNamesKey]; -} - -#pragma mark - 归档属性白名单配置 -+ (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames -{ - [self mj_setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey]; -} - -+ (NSMutableArray *)mj_totalAllowedCodingPropertyNames -{ - return [self mj_totalObjectsWithSelector:@selector(mj_allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey]; -} -#pragma mark - block和方法处理:存储block的返回值 -+ (void)mj_setupBlockReturnValue:(id (^)())block key:(const char *)key -{ - if (block) { - objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } else { - objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - - // 清空数据 - [[self dictForKey:key] removeAllObjects]; -} - -+ (NSMutableArray *)mj_totalObjectsWithSelector:(SEL)selector key:(const char *)key -{ - NSMutableArray *array = [self dictForKey:key][NSStringFromClass(self)]; - if (array) return array; - - // 创建、存储 - [self dictForKey:key][NSStringFromClass(self)] = array = [NSMutableArray array]; - - if ([self respondsToSelector:selector]) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - NSArray *subArray = [self performSelector:selector]; -#pragma clang diagnostic pop - if (subArray) { - [array addObjectsFromArray:subArray]; - } - } - - [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { - NSArray *subArray = objc_getAssociatedObject(c, key); - [array addObjectsFromArray:subArray]; - }]; - return array; -} -@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJCoding.h b/Pods/MJExtension/MJExtension/NSObject+MJCoding.h deleted file mode 100755 index d4327dd..0000000 --- a/Pods/MJExtension/MJExtension/NSObject+MJCoding.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// NSObject+MJCoding.h -// MJExtension -// -// Created by mj on 14-1-15. -// Copyright (c) 2014年 小码哥. All rights reserved. -// - -#import -#import "MJExtensionConst.h" - -/** - * Codeing协议 - */ -@protocol MJCoding -@optional -/** - * 这个数组中的属性名才会进行归档 - */ -+ (NSArray *)mj_allowedCodingPropertyNames; -/** - * 这个数组中的属性名将会被忽略:不进行归档 - */ -+ (NSArray *)mj_ignoredCodingPropertyNames; -@end - -@interface NSObject (MJCoding) -/** - * 解码(从文件中解析对象) - */ -- (void)mj_decode:(NSCoder *)decoder; -/** - * 编码(将对象写入文件中) - */ -- (void)mj_encode:(NSCoder *)encoder; -@end - -/** - 归档的实现 - */ -#define MJCodingImplementation \ -- (id)initWithCoder:(NSCoder *)decoder \ -{ \ -if (self = [super init]) { \ -[self mj_decode:decoder]; \ -} \ -return self; \ -} \ -\ -- (void)encodeWithCoder:(NSCoder *)encoder \ -{ \ -[self mj_encode:encoder]; \ -} - -#define MJExtensionCodingImplementation MJCodingImplementation \ No newline at end of file diff --git a/Pods/MJExtension/MJExtension/NSObject+MJCoding.m b/Pods/MJExtension/MJExtension/NSObject+MJCoding.m deleted file mode 100755 index 27ae8ee..0000000 --- a/Pods/MJExtension/MJExtension/NSObject+MJCoding.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// NSObject+MJCoding.m -// MJExtension -// -// Created by mj on 14-1-15. -// Copyright (c) 2014年 小码哥. All rights reserved. -// - -#import "NSObject+MJCoding.h" -#import "NSObject+MJClass.h" -#import "NSObject+MJProperty.h" -#import "MJProperty.h" - -@implementation NSObject (MJCoding) - -- (void)mj_encode:(NSCoder *)encoder -{ - Class clazz = [self class]; - - NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; - NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; - - [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { - // 检测是否被忽略 - if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; - if ([ignoredCodingPropertyNames containsObject:property.name]) return; - - id value = [property valueForObject:self]; - if (value == nil) return; - [encoder encodeObject:value forKey:property.name]; - }]; -} - -- (void)mj_decode:(NSCoder *)decoder -{ - Class clazz = [self class]; - - NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; - NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; - - [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { - // 检测是否被忽略 - if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; - if ([ignoredCodingPropertyNames containsObject:property.name]) return; - - id value = [decoder decodeObjectForKey:property.name]; - if (value == nil) { // 兼容以前的MJExtension版本 - value = [decoder decodeObjectForKey:[@"_" stringByAppendingString:property.name]]; - } - if (value == nil) return; - [property setValue:value forObject:self]; - }]; -} -@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.h b/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.h deleted file mode 100755 index e231940..0000000 --- a/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.h +++ /dev/null @@ -1,225 +0,0 @@ -// -// NSObject+MJKeyValue.h -// MJExtension -// -// Created by mj on 13-8-24. -// Copyright (c) 2013年 小码哥. All rights reserved. -// - -#import -#import "MJExtensionConst.h" -#import -#import "MJProperty.h" - -/** - * KeyValue协议 - */ -@protocol MJKeyValue -@optional -/** - * 只有这个数组中的属性名才允许进行字典和模型的转换 - */ -+ (NSArray *)mj_allowedPropertyNames; - -/** - * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 - */ -+ (NSArray *)mj_ignoredPropertyNames; - -/** - * 将属性名换为其他key去字典中取值 - * - * @return 字典中的key是属性名,value是从字典中取值用的key - */ -+ (NSDictionary *)mj_replacedKeyFromPropertyName; - -/** - * 将属性名换为其他key去字典中取值 - * - * @return 从字典中取值用的key - */ -+ (NSString *)mj_replacedKeyFromPropertyName121:(NSString *)propertyName; - -/** - * 数组中需要转换的模型类 - * - * @return 字典中的key是数组属性名,value是数组中存放模型的Class(Class类型或者NSString类型) - */ -+ (NSDictionary *)mj_objectClassInArray; - -/** - * 旧值换新值,用于过滤字典中的值 - * - * @param oldValue 旧值 - * - * @return 新值 - */ -- (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property; - -/** - * 当字典转模型完毕时调用 - */ -- (void)mj_keyValuesDidFinishConvertingToObject; - -/** - * 当模型转字典完毕时调用 - */ -- (void)mj_objectDidFinishConvertingToKeyValues; -@end - -@interface NSObject (MJKeyValue) -#pragma mark - 类方法 -/** - * 字典转模型过程中遇到的错误 - */ -+ (NSError *)mj_error; - -/** - * 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) - */ -+ (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference; - -#pragma mark - 对象方法 -/** - * 将字典的键值对转成模型属性 - * @param keyValues 字典(可以是NSDictionary、NSData、NSString) - */ -- (instancetype)mj_setKeyValues:(id)keyValues; - -/** - * 将字典的键值对转成模型属性 - * @param keyValues 字典(可以是NSDictionary、NSData、NSString) - * @param context CoreData上下文 - */ -- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; - -/** - * 将模型转成字典 - * @return 字典 - */ -- (NSMutableDictionary *)mj_keyValues; -- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys; -- (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys; - -/** - * 通过模型数组来创建一个字典数组 - * @param objectArray 模型数组 - * @return 字典数组 - */ -+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray; -+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys; -+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys; - -#pragma mark - 字典转模型 -/** - * 通过字典来创建一个模型 - * @param keyValues 字典(可以是NSDictionary、NSData、NSString) - * @return 新建的对象 - */ -+ (instancetype)mj_objectWithKeyValues:(id)keyValues; - -/** - * 通过字典来创建一个CoreData模型 - * @param keyValues 字典(可以是NSDictionary、NSData、NSString) - * @param context CoreData上下文 - * @return 新建的对象 - */ -+ (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; - -/** - * 通过plist来创建一个模型 - * @param filename 文件名(仅限于mainBundle中的文件) - * @return 新建的对象 - */ -+ (instancetype)mj_objectWithFilename:(NSString *)filename; - -/** - * 通过plist来创建一个模型 - * @param file 文件全路径 - * @return 新建的对象 - */ -+ (instancetype)mj_objectWithFile:(NSString *)file; - -#pragma mark - 字典数组转模型数组 -/** - * 通过字典数组来创建一个模型数组 - * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) - * @return 模型数组 - */ -+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray; - -/** - * 通过字典数组来创建一个模型数组 - * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) - * @param context CoreData上下文 - * @return 模型数组 - */ -+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context; - -/** - * 通过plist来创建一个模型数组 - * @param filename 文件名(仅限于mainBundle中的文件) - * @return 模型数组 - */ -+ (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename; - -/** - * 通过plist来创建一个模型数组 - * @param file 文件全路径 - * @return 模型数组 - */ -+ (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file; - -#pragma mark - 转换为JSON -/** - * 转换为JSON Data - */ -- (NSData *)mj_JSONData; -/** - * 转换为字典或者数组 - */ -- (id)mj_JSONObject; -/** - * 转换为JSON 字符串 - */ -- (NSString *)mj_JSONString; -@end - -@interface NSObject (MJKeyValueDeprecated_v_2_5_16) -- (instancetype)setKeyValues:(id)keyValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (instancetype)setKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSMutableDictionary *)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSMutableDictionary *)keyValuesWithError:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (instancetype)objectWithKeyValues:(id)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (instancetype)objectWithKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (instancetype)objectWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (instancetype)objectWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (instancetype)objectWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (instancetype)objectWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)objectArrayWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)objectArrayWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSData *)JSONData MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (id)JSONObject MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSString *)JSONString MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.m b/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.m deleted file mode 100755 index 5635e29..0000000 --- a/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.m +++ /dev/null @@ -1,708 +0,0 @@ -// -// NSObject+MJKeyValue.m -// MJExtension -// -// Created by mj on 13-8-24. -// Copyright (c) 2013年 小码哥. All rights reserved. -// - -#import "NSObject+MJKeyValue.h" -#import "NSObject+MJProperty.h" -#import "NSString+MJExtension.h" -#import "MJProperty.h" -#import "MJPropertyType.h" -#import "MJExtensionConst.h" -#import "MJFoundation.h" -#import "NSString+MJExtension.h" -#import "NSObject+MJClass.h" - -@implementation NSObject (MJKeyValue) - -#pragma mark - 错误 -static const char MJErrorKey = '\0'; -+ (NSError *)mj_error -{ - return objc_getAssociatedObject(self, &MJErrorKey); -} - -+ (void)setMj_error:(NSError *)error -{ - objc_setAssociatedObject(self, &MJErrorKey, error, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -#pragma mark - 模型 -> 字典时的参考 -/** 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) */ -static const char MJReferenceReplacedKeyWhenCreatingKeyValuesKey = '\0'; - -+ (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference -{ - objc_setAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey, @(reference), OBJC_ASSOCIATION_ASSIGN); -} - -+ (BOOL)mj_isReferenceReplacedKeyWhenCreatingKeyValues -{ - __block id value = objc_getAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); - if (!value) { - [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { - value = objc_getAssociatedObject(c, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); - - if (value) *stop = YES; - }]; - } - return [value boolValue]; -} - -#pragma mark - --常用的对象-- -static NSNumberFormatter *numberFormatter_; -+ (void)load -{ - numberFormatter_ = [[NSNumberFormatter alloc] init]; - - // 默认设置 - [self mj_referenceReplacedKeyWhenCreatingKeyValues:YES]; -} - -#pragma mark - --公共方法-- -#pragma mark - 字典 -> 模型 -- (instancetype)mj_setKeyValues:(id)keyValues -{ - return [self mj_setKeyValues:keyValues context:nil]; -} - -/** - 核心代码: - */ -- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context -{ - // 获得JSON对象 - keyValues = [keyValues mj_JSONObject]; - - MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], self, [self class], @"keyValues参数不是一个字典"); - - Class clazz = [self class]; - NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; - NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames]; - - //通过封装的方法回调一个通过运行时编写的,用于返回属性列表的方法。 - [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { - @try { - // 0.检测是否被忽略 - if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; - if ([ignoredPropertyNames containsObject:property.name]) return; - - // 1.取出属性值 - id value; - NSArray *propertyKeyses = [property propertyKeysForClass:clazz]; - for (NSArray *propertyKeys in propertyKeyses) { - value = keyValues; - for (MJPropertyKey *propertyKey in propertyKeys) { - value = [propertyKey valueInObject:value]; - } - if (value) break; - } - - // 值的过滤 - id newValue = [clazz mj_getNewValueFromObject:self oldValue:value property:property]; - if (newValue != value) { // 有过滤后的新值 - [property setValue:newValue forObject:self]; - return; - } - - // 如果没有值,就直接返回 - if (!value || value == [NSNull null]) return; - - // 2.复杂处理 - MJPropertyType *type = property.type; - Class propertyClass = type.typeClass; - Class objectClass = [property objectClassInArrayForClass:[self class]]; - - // 不可变 -> 可变处理 - if (propertyClass == [NSMutableArray class] && [value isKindOfClass:[NSArray class]]) { - value = [NSMutableArray arrayWithArray:value]; - } else if (propertyClass == [NSMutableDictionary class] && [value isKindOfClass:[NSDictionary class]]) { - value = [NSMutableDictionary dictionaryWithDictionary:value]; - } else if (propertyClass == [NSMutableString class] && [value isKindOfClass:[NSString class]]) { - value = [NSMutableString stringWithString:value]; - } else if (propertyClass == [NSMutableData class] && [value isKindOfClass:[NSData class]]) { - value = [NSMutableData dataWithData:value]; - } - - if (!type.isFromFoundation && propertyClass) { // 模型属性 - value = [propertyClass mj_objectWithKeyValues:value context:context]; - } else if (objectClass) { - if (objectClass == [NSURL class] && [value isKindOfClass:[NSArray class]]) { - // string array -> url array - NSMutableArray *urlArray = [NSMutableArray array]; - for (NSString *string in value) { - if (![string isKindOfClass:[NSString class]]) continue; - [urlArray addObject:string.mj_url]; - } - value = urlArray; - } else { // 字典数组-->模型数组 - value = [objectClass mj_objectArrayWithKeyValuesArray:value context:context]; - } - } else { - if (propertyClass == [NSString class]) { - if ([value isKindOfClass:[NSNumber class]]) { - // NSNumber -> NSString - value = [value description]; - } else if ([value isKindOfClass:[NSURL class]]) { - // NSURL -> NSString - value = [value absoluteString]; - } - } else if ([value isKindOfClass:[NSString class]]) { - if (propertyClass == [NSURL class]) { - // NSString -> NSURL - // 字符串转码 - value = [value mj_url]; - } else if (type.isNumberType) { - NSString *oldValue = value; - - // NSString -> NSNumber - if (type.typeClass == [NSDecimalNumber class]) { - value = [NSDecimalNumber decimalNumberWithString:oldValue]; - } else { - value = [numberFormatter_ numberFromString:oldValue]; - } - - // 如果是BOOL - if (type.isBoolType) { - // 字符串转BOOL(字符串没有charValue方法) - // 系统会调用字符串的charValue转为BOOL类型 - NSString *lower = [oldValue lowercaseString]; - if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) { - value = @YES; - } else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) { - value = @NO; - } - } - } - } - - // value和property类型不匹配 - if (propertyClass && ![value isKindOfClass:propertyClass]) { - value = nil; - } - } - - // 3.赋值 - [property setValue:value forObject:self]; - } @catch (NSException *exception) { - MJExtensionBuildError([self class], exception.reason); - MJExtensionLog(@"%@", exception); - } - }]; - - // 转换完毕 - if ([self respondsToSelector:@selector(mj_keyValuesDidFinishConvertingToObject)]) { - [self mj_keyValuesDidFinishConvertingToObject]; - } - return self; -} - -+ (instancetype)mj_objectWithKeyValues:(id)keyValues -{ - return [self mj_objectWithKeyValues:keyValues context:nil]; -} - -+ (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context -{ - // 获得JSON对象 - keyValues = [keyValues mj_JSONObject]; - MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], nil, [self class], @"keyValues参数不是一个字典"); - - if ([self isSubclassOfClass:[NSManagedObject class]] && context) { - return [[NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(self) inManagedObjectContext:context] mj_setKeyValues:keyValues context:context]; - } - return [[[self alloc] init] mj_setKeyValues:keyValues]; -} - -+ (instancetype)mj_objectWithFilename:(NSString *)filename -{ - MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil"); - - return [self mj_objectWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]]; -} - -+ (instancetype)mj_objectWithFile:(NSString *)file -{ - MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil"); - - return [self mj_objectWithKeyValues:[NSDictionary dictionaryWithContentsOfFile:file]]; -} - -#pragma mark - 字典数组 -> 模型数组 -+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(NSArray *)keyValuesArray -{ - return [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:nil]; -} - -+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context -{ - // 如果是JSON字符串 - keyValuesArray = [keyValuesArray mj_JSONObject]; - - // 1.判断真实性 - MJExtensionAssertError([keyValuesArray isKindOfClass:[NSArray class]], nil, [self class], @"keyValuesArray参数不是一个数组"); - - // 如果数组里面放的是NSString、NSNumber等数据 - if ([MJFoundation isClassFromFoundation:self]) return [NSMutableArray arrayWithArray:keyValuesArray]; - - - // 2.创建数组 - NSMutableArray *modelArray = [NSMutableArray array]; - - // 3.遍历 - for (NSDictionary *keyValues in keyValuesArray) { - if ([keyValues isKindOfClass:[NSArray class]]){ - [modelArray addObject:[self mj_objectArrayWithKeyValuesArray:keyValues context:context]]; - } else { - id model = [self mj_objectWithKeyValues:keyValues context:context]; - if (model) [modelArray addObject:model]; - } - } - - return modelArray; -} - -+ (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename -{ - MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil"); - - return [self mj_objectArrayWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]]; -} - -+ (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file -{ - MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil"); - - return [self mj_objectArrayWithKeyValuesArray:[NSArray arrayWithContentsOfFile:file]]; -} - -#pragma mark - 模型 -> 字典 -- (NSMutableDictionary *)mj_keyValues -{ - return [self mj_keyValuesWithKeys:nil ignoredKeys:nil]; -} - -- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys -{ - return [self mj_keyValuesWithKeys:keys ignoredKeys:nil]; -} - -- (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys -{ - return [self mj_keyValuesWithKeys:nil ignoredKeys:ignoredKeys]; -} - -- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys -{ - // 如果自己不是模型类, 那就返回自己 - MJExtensionAssertError(![MJFoundation isClassFromFoundation:[self class]], (NSMutableDictionary *)self, [self class], @"不是自定义的模型类") - - id keyValues = [NSMutableDictionary dictionary]; - - Class clazz = [self class]; - NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; - NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames]; - - [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { - @try { - // 0.检测是否被忽略 - if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; - if ([ignoredPropertyNames containsObject:property.name]) return; - if (keys.count && ![keys containsObject:property.name]) return; - if ([ignoredKeys containsObject:property.name]) return; - - // 1.取出属性值 - id value = [property valueForObject:self]; - if (!value) return; - - // 2.如果是模型属性 - MJPropertyType *type = property.type; - Class propertyClass = type.typeClass; - if (!type.isFromFoundation && propertyClass) { - value = [value mj_keyValues]; - } else if ([value isKindOfClass:[NSArray class]]) { - // 3.处理数组里面有模型的情况 - value = [NSObject mj_keyValuesArrayWithObjectArray:value]; - } else if (propertyClass == [NSURL class]) { - value = [value absoluteString]; - } - - // 4.赋值 - if ([clazz mj_isReferenceReplacedKeyWhenCreatingKeyValues]) { - NSArray *propertyKeys = [[property propertyKeysForClass:clazz] firstObject]; - NSUInteger keyCount = propertyKeys.count; - // 创建字典 - __block id innerContainer = keyValues; - [propertyKeys enumerateObjectsUsingBlock:^(MJPropertyKey *propertyKey, NSUInteger idx, BOOL *stop) { - // 下一个属性 - MJPropertyKey *nextPropertyKey = nil; - if (idx != keyCount - 1) { - nextPropertyKey = propertyKeys[idx + 1]; - } - - if (nextPropertyKey) { // 不是最后一个key - // 当前propertyKey对应的字典或者数组 - id tempInnerContainer = [propertyKey valueInObject:innerContainer]; - if (tempInnerContainer == nil || [tempInnerContainer isKindOfClass:[NSNull class]]) { - if (nextPropertyKey.type == MJPropertyKeyTypeDictionary) { - tempInnerContainer = [NSMutableDictionary dictionary]; - } else { - tempInnerContainer = [NSMutableArray array]; - } - if (propertyKey.type == MJPropertyKeyTypeDictionary) { - innerContainer[propertyKey.name] = tempInnerContainer; - } else { - innerContainer[propertyKey.name.intValue] = tempInnerContainer; - } - } - - if ([tempInnerContainer isKindOfClass:[NSMutableArray class]]) { - NSMutableArray *tempInnerContainerArray = tempInnerContainer; - int index = nextPropertyKey.name.intValue; - while (tempInnerContainerArray.count < index + 1) { - [tempInnerContainerArray addObject:[NSNull null]]; - } - } - - innerContainer = tempInnerContainer; - } else { // 最后一个key - if (propertyKey.type == MJPropertyKeyTypeDictionary) { - innerContainer[propertyKey.name] = value; - } else { - innerContainer[propertyKey.name.intValue] = value; - } - } - }]; - } else { - keyValues[property.name] = value; - } - } @catch (NSException *exception) { - MJExtensionBuildError([self class], exception.reason); - MJExtensionLog(@"%@", exception); - } - }]; - - // 转换完毕 - if ([self respondsToSelector:@selector(mj_objectDidFinishConvertingToKeyValues)]) { - [self mj_objectDidFinishConvertingToKeyValues]; - } - - return keyValues; -} -#pragma mark - 模型数组 -> 字典数组 -+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray -{ - return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:nil]; -} - -+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys -{ - return [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys ignoredKeys:nil]; -} - -+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys -{ - return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:ignoredKeys]; -} - -+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys -{ - // 0.判断真实性 - MJExtensionAssertError([objectArray isKindOfClass:[NSArray class]], nil, [self class], @"objectArray参数不是一个数组"); - - // 1.创建数组 - NSMutableArray *keyValuesArray = [NSMutableArray array]; - for (id object in objectArray) { - if (keys) { - [keyValuesArray addObject:[object mj_keyValuesWithKeys:keys]]; - } else { - [keyValuesArray addObject:[object mj_keyValuesWithIgnoredKeys:ignoredKeys]]; - } - } - return keyValuesArray; -} - -#pragma mark - 转换为JSON -- (NSData *)mj_JSONData -{ - if ([self isKindOfClass:[NSString class]]) { - return [((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding]; - } else if ([self isKindOfClass:[NSData class]]) { - return (NSData *)self; - } - - return [NSJSONSerialization dataWithJSONObject:[self mj_JSONObject] options:kNilOptions error:nil]; -} - -- (id)mj_JSONObject -{ - if ([self isKindOfClass:[NSString class]]) { - return [NSJSONSerialization JSONObjectWithData:[((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil]; - } else if ([self isKindOfClass:[NSData class]]) { - return [NSJSONSerialization JSONObjectWithData:(NSData *)self options:kNilOptions error:nil]; - } - - return self.mj_keyValues; -} - -- (NSString *)mj_JSONString -{ - if ([self isKindOfClass:[NSString class]]) { - return (NSString *)self; - } else if ([self isKindOfClass:[NSData class]]) { - return [[NSString alloc] initWithData:(NSData *)self encoding:NSUTF8StringEncoding]; - } - - return [[NSString alloc] initWithData:[self mj_JSONData] encoding:NSUTF8StringEncoding]; -} -@end - -@implementation NSObject (MJKeyValueDeprecated_v_2_5_16) -- (instancetype)setKeyValues:(id)keyValues -{ - return [self mj_setKeyValues:keyValues]; -} - -- (instancetype)setKeyValues:(id)keyValues error:(NSError **)error -{ - id value = [self mj_setKeyValues:keyValues]; - if (error != NULL) { - *error = [self.class mj_error]; - } - return value; - -} - -- (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context -{ - return [self mj_setKeyValues:keyValues context:context]; -} - -- (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error -{ - id value = [self mj_setKeyValues:keyValues context:context]; - if (error != NULL) { - *error = [self.class mj_error]; - } - return value; -} - -+ (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference -{ - [self mj_referenceReplacedKeyWhenCreatingKeyValues:reference]; -} - -- (NSMutableDictionary *)keyValues -{ - return [self mj_keyValues]; -} - -- (NSMutableDictionary *)keyValuesWithError:(NSError **)error -{ - id value = [self mj_keyValues]; - if (error != NULL) { - *error = [self.class mj_error]; - } - return value; -} - -- (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys -{ - return [self mj_keyValuesWithKeys:keys]; -} - -- (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError **)error -{ - id value = [self mj_keyValuesWithKeys:keys]; - if (error != NULL) { - *error = [self.class mj_error]; - } - return value; -} - -- (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys -{ - return [self mj_keyValuesWithIgnoredKeys:ignoredKeys]; -} - -- (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError **)error -{ - id value = [self mj_keyValuesWithIgnoredKeys:ignoredKeys]; - if (error != NULL) { - *error = [self.class mj_error]; - } - return value; -} - -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray -{ - return [self mj_keyValuesArrayWithObjectArray:objectArray]; -} - -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError **)error -{ - id value = [self mj_keyValuesArrayWithObjectArray:objectArray]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys -{ - return [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys]; -} - -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError **)error -{ - id value = [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys -{ - return [self mj_keyValuesArrayWithObjectArray:objectArray ignoredKeys:ignoredKeys]; -} - -+ (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError **)error -{ - id value = [self mj_keyValuesArrayWithObjectArray:objectArray ignoredKeys:ignoredKeys]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -+ (instancetype)objectWithKeyValues:(id)keyValues -{ - return [self mj_objectWithKeyValues:keyValues]; -} - -+ (instancetype)objectWithKeyValues:(id)keyValues error:(NSError **)error -{ - id value = [self mj_objectWithKeyValues:keyValues]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -+ (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context -{ - return [self mj_objectWithKeyValues:keyValues context:context]; -} - -+ (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error -{ - id value = [self mj_objectWithKeyValues:keyValues context:context]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -+ (instancetype)objectWithFilename:(NSString *)filename -{ - return [self mj_objectWithFilename:filename]; -} - -+ (instancetype)objectWithFilename:(NSString *)filename error:(NSError **)error -{ - id value = [self mj_objectWithFilename:filename]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -+ (instancetype)objectWithFile:(NSString *)file -{ - return [self mj_objectWithFile:file]; -} - -+ (instancetype)objectWithFile:(NSString *)file error:(NSError **)error -{ - id value = [self mj_objectWithFile:file]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -+ (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray -{ - return [self mj_objectArrayWithKeyValuesArray:keyValuesArray]; -} - -+ (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray error:(NSError **)error -{ - id value = [self mj_objectArrayWithKeyValuesArray:keyValuesArray]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -+ (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context -{ - return [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:context]; -} - -+ (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError **)error -{ - id value = [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:context]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -+ (NSMutableArray *)objectArrayWithFilename:(NSString *)filename -{ - return [self mj_objectArrayWithFilename:filename]; -} - -+ (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError **)error -{ - id value = [self mj_objectArrayWithFilename:filename]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -+ (NSMutableArray *)objectArrayWithFile:(NSString *)file -{ - return [self mj_objectArrayWithFile:file]; -} - -+ (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError **)error -{ - id value = [self mj_objectArrayWithFile:file]; - if (error != NULL) { - *error = [self mj_error]; - } - return value; -} - -- (NSData *)JSONData -{ - return [self mj_JSONData]; -} - -- (id)JSONObject -{ - return [self mj_JSONObject]; -} - -- (NSString *)JSONString -{ - return [self mj_JSONString]; -} -@end \ No newline at end of file diff --git a/Pods/MJExtension/MJExtension/NSObject+MJProperty.h b/Pods/MJExtension/MJExtension/NSObject+MJProperty.h deleted file mode 100644 index 1a64732..0000000 --- a/Pods/MJExtension/MJExtension/NSObject+MJProperty.h +++ /dev/null @@ -1,79 +0,0 @@ -// -// NSObject+MJProperty.h -// MJExtensionExample -// -// Created by MJ Lee on 15/4/17. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import -#import "MJExtensionConst.h" - -@class MJProperty; - -/** - * 遍历成员变量用的block - * - * @param property 成员的包装对象 - * @param stop YES代表停止遍历,NO代表继续遍历 - */ -typedef void (^MJPropertiesEnumeration)(MJProperty *property, BOOL *stop); - -/** 将属性名换为其他key去字典中取值 */ -typedef NSDictionary * (^MJReplacedKeyFromPropertyName)(); -typedef NSString * (^MJReplacedKeyFromPropertyName121)(NSString *propertyName); -/** 数组中需要转换的模型类 */ -typedef NSDictionary * (^MJObjectClassInArray)(); -/** 用于过滤字典中的值 */ -typedef id (^MJNewValueFromOldValue)(id object, id oldValue, MJProperty *property); - -/** - * 成员属性相关的扩展 - */ -@interface NSObject (MJProperty) -#pragma mark - 遍历 -/** - * 遍历所有的成员 - */ -+ (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration; - -#pragma mark - 新值配置 -/** - * 用于过滤字典中的值 - * - * @param newValueFormOldValue 用于过滤字典中的值 - */ -+ (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue; -+ (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property; - -#pragma mark - key配置 -/** - * 将属性名换为其他key去字典中取值 - * - * @param replacedKeyFromPropertyName 将属性名换为其他key去字典中取值 - */ -+ (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName; -/** - * 将属性名换为其他key去字典中取值 - * - * @param replacedKeyFromPropertyName121 将属性名换为其他key去字典中取值 - */ -+ (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121; - -#pragma mark - array model class配置 -/** - * 数组中需要转换的模型类 - * - * @param objectClassInArray 数组中需要转换的模型类 - */ -+ (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray; -@end - -@interface NSObject (MJPropertyDeprecated_v_2_5_16) -+ (void)enumerateProperties:(MJPropertiesEnumeration)enumeration MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (void)setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (id)getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (void)setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (void)setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121 MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -+ (void)setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -@end \ No newline at end of file diff --git a/Pods/MJExtension/MJExtension/NSObject+MJProperty.m b/Pods/MJExtension/MJExtension/NSObject+MJProperty.m deleted file mode 100644 index 9ac6134..0000000 --- a/Pods/MJExtension/MJExtension/NSObject+MJProperty.m +++ /dev/null @@ -1,271 +0,0 @@ -// -// NSObject+MJProperty.m -// MJExtensionExample -// -// Created by MJ Lee on 15/4/17. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "NSObject+MJProperty.h" -#import "NSObject+MJKeyValue.h" -#import "NSObject+MJCoding.h" -#import "NSObject+MJClass.h" -#import "MJProperty.h" -#import "MJFoundation.h" -#import - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wundeclared-selector" -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - -static const char MJReplacedKeyFromPropertyNameKey = '\0'; -static const char MJReplacedKeyFromPropertyName121Key = '\0'; -static const char MJNewValueFromOldValueKey = '\0'; -static const char MJObjectClassInArrayKey = '\0'; - -static const char MJCachedPropertiesKey = '\0'; - -@implementation NSObject (Property) - -static NSMutableDictionary *replacedKeyFromPropertyNameDict_; -static NSMutableDictionary *replacedKeyFromPropertyName121Dict_; -static NSMutableDictionary *newValueFromOldValueDict_; -static NSMutableDictionary *objectClassInArrayDict_; -static NSMutableDictionary *cachedPropertiesDict_; - -+ (void)load -{ - replacedKeyFromPropertyNameDict_ = [NSMutableDictionary dictionary]; - replacedKeyFromPropertyName121Dict_ = [NSMutableDictionary dictionary]; - newValueFromOldValueDict_ = [NSMutableDictionary dictionary]; - objectClassInArrayDict_ = [NSMutableDictionary dictionary]; - cachedPropertiesDict_ = [NSMutableDictionary dictionary]; -} - -+ (NSMutableDictionary *)dictForKey:(const void *)key -{ - if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict_; - if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict_; - if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict_; - if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict_; - if (key == &MJCachedPropertiesKey) return cachedPropertiesDict_; - return nil; -} - -#pragma mark - --私有方法-- -+ (NSString *)propertyKey:(NSString *)propertyName -{ - MJExtensionAssertParamNotNil2(propertyName, nil); - - __block NSString *key = nil; - // 查看有没有需要替换的key - if ([self respondsToSelector:@selector(mj_replacedKeyFromPropertyName121:)]) { - key = [self mj_replacedKeyFromPropertyName121:propertyName]; - } - // 兼容旧版本 - if ([self respondsToSelector:@selector(replacedKeyFromPropertyName121:)]) { - key = [self performSelector:@selector(replacedKeyFromPropertyName121) withObject:propertyName]; - } - - // 调用block - if (!key) { - [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { - MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key); - if (block) { - key = block(propertyName); - } - if (key) *stop = YES; - }]; - } - - // 查看有没有需要替换的key - if (!key && [self respondsToSelector:@selector(mj_replacedKeyFromPropertyName)]) { - key = [self mj_replacedKeyFromPropertyName][propertyName]; - } - // 兼容旧版本 - if (!key && [self respondsToSelector:@selector(replacedKeyFromPropertyName)]) { - key = [self performSelector:@selector(replacedKeyFromPropertyName)][propertyName]; - } - - if (!key) { - [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { - NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey); - if (dict) { - key = dict[propertyName]; - } - if (key) *stop = YES; - }]; - } - - // 2.用属性名作为key - if (!key) key = propertyName; - - return key; -} - -+ (Class)propertyObjectClassInArray:(NSString *)propertyName -{ - __block id clazz = nil; - if ([self respondsToSelector:@selector(mj_objectClassInArray)]) { - clazz = [self mj_objectClassInArray][propertyName]; - } - // 兼容旧版本 - if ([self respondsToSelector:@selector(objectClassInArray)]) { - clazz = [self performSelector:@selector(objectClassInArray)][propertyName]; - } - - if (!clazz) { - [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { - NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey); - if (dict) { - clazz = dict[propertyName]; - } - if (clazz) *stop = YES; - }]; - } - - // 如果是NSString类型 - if ([clazz isKindOfClass:[NSString class]]) { - clazz = NSClassFromString(clazz); - } - return clazz; -} - -#pragma mark - --公共方法-- -+ (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration -{ - // 获得成员变量 - NSArray *cachedProperties = [self properties]; - - // 遍历成员变量 - BOOL stop = NO; - for (MJProperty *property in cachedProperties) { - enumeration(property, &stop); - if (stop) break; - } -} - -#pragma mark - 公共方法 -+ (NSMutableArray *)properties -{ - NSMutableArray *cachedProperties = [self dictForKey:&MJCachedPropertiesKey][NSStringFromClass(self)]; - - if (cachedProperties == nil) { - cachedProperties = [NSMutableArray array]; - - [self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) { - // 1.获得所有的成员变量 - unsigned int outCount = 0; - objc_property_t *properties = class_copyPropertyList(c, &outCount); - - // 2.遍历每一个成员变量 - for (unsigned int i = 0; i -#import "MJExtensionConst.h" - -@interface NSString (MJExtension) -/** - * 驼峰转下划线(loveYou -> love_you) - */ -- (NSString *)mj_underlineFromCamel; -/** - * 下划线转驼峰(love_you -> loveYou) - */ -- (NSString *)mj_camelFromUnderline; -/** - * 首字母变大写 - */ -- (NSString *)mj_firstCharUpper; -/** - * 首字母变小写 - */ -- (NSString *)mj_firstCharLower; - -- (BOOL)mj_isPureInt; - -- (NSURL *)mj_url; -@end - -@interface NSString (MJExtensionDeprecated_v_2_5_16) -- (NSString *)underlineFromCamel MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSString *)camelFromUnderline MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSString *)firstCharUpper MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSString *)firstCharLower MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (BOOL)isPureInt MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -- (NSURL *)url MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); -@end diff --git a/Pods/MJExtension/MJExtension/NSString+MJExtension.m b/Pods/MJExtension/MJExtension/NSString+MJExtension.m deleted file mode 100644 index 050a061..0000000 --- a/Pods/MJExtension/MJExtension/NSString+MJExtension.m +++ /dev/null @@ -1,110 +0,0 @@ -// -// NSString+MJExtension.m -// MJExtensionExample -// -// Created by MJ Lee on 15/6/7. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "NSString+MJExtension.h" - -@implementation NSString (MJExtension) -- (NSString *)mj_underlineFromCamel -{ - if (self.length == 0) return self; - NSMutableString *string = [NSMutableString string]; - for (NSUInteger i = 0; i= 2) [string appendString:[cmp substringFromIndex:1]]; - } else { - [string appendString:cmp]; - } - } - return string; -} - -- (NSString *)mj_firstCharLower -{ - if (self.length == 0) return self; - NSMutableString *string = [NSMutableString string]; - [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].lowercaseString]; - if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; - return string; -} - -- (NSString *)mj_firstCharUpper -{ - if (self.length == 0) return self; - NSMutableString *string = [NSMutableString string]; - [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].uppercaseString]; - if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; - return string; -} - -- (BOOL)mj_isPureInt -{ - NSScanner *scan = [NSScanner scannerWithString:self]; - int val; - return [scan scanInt:&val] && [scan isAtEnd]; -} - -- (NSURL *)mj_url -{ -// [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]]; - - return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))]; -} -@end - -@implementation NSString (MJExtensionDeprecated_v_2_5_16) -- (NSString *)underlineFromCamel -{ - return self.mj_underlineFromCamel; -} - -- (NSString *)camelFromUnderline -{ - return self.mj_camelFromUnderline; -} - -- (NSString *)firstCharLower -{ - return self.mj_firstCharLower; -} - -- (NSString *)firstCharUpper -{ - return self.mj_firstCharUpper; -} - -- (BOOL)isPureInt -{ - return self.mj_isPureInt; -} - -- (NSURL *)url -{ - return self.mj_url; -} -@end diff --git a/Pods/MJExtension/README.md b/Pods/MJExtension/README.md deleted file mode 100644 index 27fb4a3..0000000 --- a/Pods/MJExtension/README.md +++ /dev/null @@ -1,541 +0,0 @@ - -![Logo](http://images.cnitblog.com/blog2015/497279/201505/051004316736641.png) -MJExtension -=== -- A fast, convenient and nonintrusive conversion between JSON and model. -- 转换速度快、使用简单方便的字典转模型框架 - -GitHub:[CoderMJLee](https://github.com/CoderMJLee) | Blog:[mjios(Chinese)](http://www.cnblogs.com/mjios) | PR is welcome,or [feedback](mailto:richermj123go@vip.qq.com) - - -## Contents -* [Getting Started 【开始使用】](#Getting_Started) - * [Features 【能做什么】](#Features) - * [Installation 【安装】](#Installation) -* [Examples 【示例】](#Examples) - * [JSON -> Model](#JSON_Model) - * [JSONString -> Model](#JSONString_Model) - * [Model contains model](#Model_contains_model) - * [Model contains model-array](#Model_contains_model_array) - * [Model name - JSON key mapping](#Model_name_JSON_key_mapping) - * [JSON array -> model array](#JSON_array_model_array) - * [Model -> JSON](#Model_JSON) - * [Model array -> JSON array](#Model_array_JSON_array) - * [Core Data](#Core_Data) - * [Coding](#Coding) - * [Camel -> underline](#Camel_underline) - * [NSString -> NSDate, nil -> @""](#NSString_NSDate) - * [More use cases](#More_use_cases) - ---- - -# Getting Started【开始使用】 - -## Features【能做什么】 -- MJExtension是一套字典和模型之间互相转换的超轻量级框架 -* `JSON` --> `Model`、`Core Data Model` -* `JSONString` --> `Model`、`Core Data Model` -* `Model`、`Core Data Model` --> `JSON` -* `JSON Array` --> `Model Array`、`Core Data Model Array` -* `JSONString` --> `Model Array`、`Core Data Model Array` -* `Model Array`、`Core Data Model Array` --> `JSON Array` -* Coding all properties of model in one line code. - * 只需要一行代码,就能实现模型的所有属性进行Coding(归档和解档) - -## Installation【安装】 - -### From CocoaPods【使用CocoaPods】 - -```ruby -pod 'MJExtension' -``` - -### Manually【手动导入】 -- Drag all source files under floder `MJExtension` to your project.【将`MJExtension`文件夹中的所有源代码拽入项目中】 -- Import the main header file:`#import "MJExtension.h"`【导入主头文件:`#import "MJExtension.h"`】 - -```objc -MJExtension.h -MJConst.h MJConst.m -MJFoundation.h MJFoundation.m -MJProperty.h MJProperty.m -MJType.h MJType.m -NSObject+MJCoding.h NSObject+MJCoding.m -NSObject+MJProperty.h NSObject+MJProperty.m -NSObject+MJKeyValue.h NSObject+MJKeyValue.m -``` - -# Examples【示例】 - -### The most simple JSON -> Model【最简单的字典转模型】 - -```objc -typedef enum { - SexMale, - SexFemale -} Sex; - -@interface User : NSObject -@property (copy, nonatomic) NSString *name; -@property (copy, nonatomic) NSString *icon; -@property (assign, nonatomic) unsigned int age; -@property (copy, nonatomic) NSString *height; -@property (strong, nonatomic) NSNumber *money; -@property (assign, nonatomic) Sex sex; -@property (assign, nonatomic, getter=isGay) BOOL gay; -@end - -/***********************************************/ - -NSDictionary *dict = @{ - @"name" : @"Jack", - @"icon" : @"lufy.png", - @"age" : @20, - @"height" : @"1.55", - @"money" : @100.9, - @"sex" : @(SexFemale), - @"gay" : @"true" -// @"gay" : @"1" -// @"gay" : @"NO" -}; - -// JSON -> User -User *user = [User mj_objectWithKeyValues:dict]; - -NSLog(@"name=%@, icon=%@, age=%zd, height=%@, money=%@, sex=%d, gay=%d", user.name, user.icon, user.age, user.height, user.money, user.sex, user.gay); -// name=Jack, icon=lufy.png, age=20, height=1.550000, money=100.9, sex=1 -``` - -### JSONString -> Model【JSON字符串转模型】 - -```objc -// 1.Define a JSONString -NSString *jsonString = @"{\"name\":\"Jack\", \"icon\":\"lufy.png\", \"age\":20}"; - -// 2.JSONString -> User -User *user = [User mj_objectWithKeyValues:jsonString]; - -// 3.Print user's properties -NSLog(@"name=%@, icon=%@, age=%d", user.name, user.icon, user.age); -// name=Jack, icon=lufy.png, age=20 -``` - -### Model contains model【模型中嵌套模型】 - -```objc -@interface Status : NSObject -@property (copy, nonatomic) NSString *text; -@property (strong, nonatomic) User *user; -@property (strong, nonatomic) Status *retweetedStatus; -@end - -/***********************************************/ - -NSDictionary *dict = @{ - @"text" : @"Agree!Nice weather!", - @"user" : @{ - @"name" : @"Jack", - @"icon" : @"lufy.png" - }, - @"retweetedStatus" : @{ - @"text" : @"Nice weather!", - @"user" : @{ - @"name" : @"Rose", - @"icon" : @"nami.png" - } - } -}; - -// JSON -> Status -Status *status = [Status mj_objectWithKeyValues:dict]; - -NSString *text = status.text; -NSString *name = status.user.name; -NSString *icon = status.user.icon; -NSLog(@"text=%@, name=%@, icon=%@", text, name, icon); -// text=Agree!Nice weather!, name=Jack, icon=lufy.png - -NSString *text2 = status.retweetedStatus.text; -NSString *name2 = status.retweetedStatus.user.name; -NSString *icon2 = status.retweetedStatus.user.icon; -NSLog(@"text2=%@, name2=%@, icon2=%@", text2, name2, icon2); -// text2=Nice weather!, name2=Rose, icon2=nami.png -``` - -### Model contains model-array【模型中有个数组属性,数组里面又要装着其他模型】 - -```objc -@interface Ad : NSObject -@property (copy, nonatomic) NSString *image; -@property (copy, nonatomic) NSString *url; -@end - -@interface StatusResult : NSObject -/** Contatins status model */ -@property (strong, nonatomic) NSMutableArray *statuses; -/** Contatins ad model */ -@property (strong, nonatomic) NSArray *ads; -@property (strong, nonatomic) NSNumber *totalNumber; -@end - -/***********************************************/ - -// Tell MJExtension what type model will be contained in statuses and ads. -[StatusResult mj_setupObjectClassInArray:^NSDictionary *{ - return @{ - @"statuses" : @"Status", - // @"statuses" : [Status class], - @"ads" : @"Ad" - // @"ads" : [Ad class] - }; -}]; -// Equals: StatusResult.m implements +mj_objectClassInArray method. - -NSDictionary *dict = @{ - @"statuses" : @[ - @{ - @"text" : @"Nice weather!", - @"user" : @{ - @"name" : @"Rose", - @"icon" : @"nami.png" - } - }, - @{ - @"text" : @"Go camping tomorrow!", - @"user" : @{ - @"name" : @"Jack", - @"icon" : @"lufy.png" - } - } - ], - @"ads" : @[ - @{ - @"image" : @"ad01.png", - @"url" : @"http://www.ad01.com/" - }, - @{ - @"image" : @"ad02.png", - @"url" : @"http://www.ad02.com/" - } - ], - @"totalNumber" : @"2014" -}; - -// JSON -> StatusResult -StatusResult *result = [StatusResult mj_objectWithKeyValues:dict]; - -NSLog(@"totalNumber=%@", result.totalNumber); -// totalNumber=2014 - -// Printing -for (Status *status in result.statuses) { - NSString *text = status.text; - NSString *name = status.user.name; - NSString *icon = status.user.icon; - NSLog(@"text=%@, name=%@, icon=%@", text, name, icon); -} -// text=Nice weather!, name=Rose, icon=nami.png -// text=Go camping tomorrow!, name=Jack, icon=lufy.png - -// Printing -for (Ad *ad in result.ads) { - NSLog(@"image=%@, url=%@", ad.image, ad.url); -} -// image=ad01.png, url=http://www.ad01.com -// image=ad02.png, url=http://www.ad02.com -``` - -### Model name - JSON key mapping【模型中的属性名和字典中的key不相同(或者需要多级映射)】 - -```objc -@interface Bag : NSObject -@property (copy, nonatomic) NSString *name; -@property (assign, nonatomic) double price; -@end - -@interface Student : NSObject -@property (copy, nonatomic) NSString *ID; -@property (copy, nonatomic) NSString *desc; -@property (copy, nonatomic) NSString *nowName; -@property (copy, nonatomic) NSString *oldName; -@property (copy, nonatomic) NSString *nameChangedTime; -@property (strong, nonatomic) Bag *bag; -@end - -/***********************************************/ - -// How to map -[Student mj_setupReplacedKeyFromPropertyName:^NSDictionary *{ - return @{ - @"ID" : @"id", - @"desc" : @"desciption", - @"oldName" : @"name.oldName", - @"nowName" : @"name.newName", - @"nameChangedTime" : @"name.info[1].nameChangedTime", - @"bag" : @"other.bag" - }; -}]; -// Equals: Student.m implements +mj_replacedKeyFromPropertyName method. - -NSDictionary *dict = @{ - @"id" : @"20", - @"desciption" : @"kids", - @"name" : @{ - @"newName" : @"lufy", - @"oldName" : @"kitty", - @"info" : @[ - @"test-data", - @{ - @"nameChangedTime" : @"2013-08" - } - ] - }, - @"other" : @{ - @"bag" : @{ - @"name" : @"a red bag", - @"price" : @100.7 - } - } -}; - -// JSON -> Student -Student *stu = [Student mj_objectWithKeyValues:dict]; - -// Printing -NSLog(@"ID=%@, desc=%@, oldName=%@, nowName=%@, nameChangedTime=%@", - stu.ID, stu.desc, stu.oldName, stu.nowName, stu.nameChangedTime); -// ID=20, desc=kids, oldName=kitty, nowName=lufy, nameChangedTime=2013-08 -NSLog(@"bagName=%@, bagPrice=%f", stu.bag.name, stu.bag.price); -// bagName=a red bag, bagPrice=100.700000 -``` - - -### JSON array -> model array【将一个字典数组转成模型数组】 - -```objc -NSArray *dictArray = @[ - @{ - @"name" : @"Jack", - @"icon" : @"lufy.png" - }, - @{ - @"name" : @"Rose", - @"icon" : @"nami.png" - } - ]; - -// JSON array -> User array -NSArray *userArray = [User mj_objectArrayWithKeyValuesArray:dictArray]; - -// Printing -for (User *user in userArray) { - NSLog(@"name=%@, icon=%@", user.name, user.icon); -} -// name=Jack, icon=lufy.png -// name=Rose, icon=nami.png -``` - -### Model -> JSON【将一个模型转成字典】 -```objc -// New model -User *user = [[User alloc] init]; -user.name = @"Jack"; -user.icon = @"lufy.png"; - -Status *status = [[Status alloc] init]; -status.user = user; -status.text = @"Nice mood!"; - -// Status -> JSON -NSDictionary *statusDict = status.mj_keyValues; -NSLog(@"%@", statusDict); -/* - { - text = "Nice mood!"; - user = { - icon = "lufy.png"; - name = Jack; - }; - } - */ - -// More complex situation -Student *stu = [[Student alloc] init]; -stu.ID = @"123"; -stu.oldName = @"rose"; -stu.nowName = @"jack"; -stu.desc = @"handsome"; -stu.nameChangedTime = @"2018-09-08"; - -Bag *bag = [[Bag alloc] init]; -bag.name = @"a red bag"; -bag.price = 205; -stu.bag = bag; - -NSDictionary *stuDict = stu.mj_keyValues; -NSLog(@"%@", stuDict); -/* -{ - ID = 123; - bag = { - name = "\U5c0f\U4e66\U5305"; - price = 205; - }; - desc = handsome; - nameChangedTime = "2018-09-08"; - nowName = jack; - oldName = rose; -} - */ -``` - -### Model array -> JSON array【将一个模型数组转成字典数组】 - -```objc -// New model array -User *user1 = [[User alloc] init]; -user1.name = @"Jack"; -user1.icon = @"lufy.png"; - -User *user2 = [[User alloc] init]; -user2.name = @"Rose"; -user2.icon = @"nami.png"; - -NSArray *userArray = @[user1, user2]; - -// Model array -> JSON array -NSArray *dictArray = [User mj_keyValuesArrayWithObjectArray:userArray]; -NSLog(@"%@", dictArray); -/* - ( - { - icon = "lufy.png"; - name = Jack; - }, - { - icon = "nami.png"; - name = Rose; - } - ) - */ -``` - -### Core Data - -```objc -NSDictionary *dict = @{ - @"name" : @"Jack", - @"icon" : @"lufy.png", - @"age" : @20, - @"height" : @1.55, - @"money" : @"100.9", - @"sex" : @(SexFemale), - @"gay" : @"true" - }; - -// This demo just provide simple steps -NSManagedObjectContext *context = nil; -User *user = [User mj_objectWithKeyValues:dict context:context]; - -[context save:nil]; -``` - -### Coding - -```objc -# import "MJExtension.h" - -@implementation Bag -// NSCoding Implementation -MJExtensionCodingImplementation -@end - -/***********************************************/ - -// what properties not to be coded -[Bag mj_setupIgnoredCodingPropertyNames:^NSArray *{ - return @[@"name"]; -}]; -// Equals: Bag.m implements +mj_ignoredCodingPropertyNames method. - -// Create model -Bag *bag = [[Bag alloc] init]; -bag.name = @"Red bag"; -bag.price = 200.8; - -NSString *file = [NSHomeDirectory() stringByAppendingPathComponent:@"Desktop/bag.data"]; -// Encoding -[NSKeyedArchiver archiveRootObject:bag toFile:file]; - -// Decoding -Bag *decodedBag = [NSKeyedUnarchiver unarchiveObjectWithFile:file]; -NSLog(@"name=%@, price=%f", decodedBag.name, decodedBag.price); -// name=(null), price=200.800000 -``` - -### Camel -> underline【统一转换属性名(比如驼峰转下划线)】 -```objc -// Dog -# import "MJExtension.h" - -@implementation Dog -+ (NSString *)mj_replacedKeyFromPropertyName121:(NSString *)propertyName -{ - // nickName -> nick_name - return [propertyName mj_underlineFromCamel]; -} -@end - -// NSDictionary -NSDictionary *dict = @{ - @"nick_name" : @"旺财", - @"sale_price" : @"10.5", - @"run_speed" : @"100.9" - }; -// NSDictionary -> Dog -Dog *dog = [Dog mj_objectWithKeyValues:dict]; - -// printing -NSLog(@"nickName=%@, scalePrice=%f runSpeed=%f", dog.nickName, dog.salePrice, dog.runSpeed); -``` - -### NSString -> NSDate, nil -> @""【过滤字典的值(比如字符串日期处理为NSDate、字符串nil处理为@"")】 -```objc -// Book -# import "MJExtension.h" - -@implementation Book -- (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property -{ - if ([property.name isEqualToString:@"publisher"]) { - if (oldValue == nil) return @""; - } else if (property.type.typeClass == [NSDate class]) { - NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; - fmt.dateFormat = @"yyyy-MM-dd"; - return [fmt dateFromString:oldValue]; - } - - return oldValue; -} -@end - -// NSDictionary -NSDictionary *dict = @{ - @"name" : @"5分钟突破iOS开发", - @"publishedTime" : @"2011-09-10" - }; -// NSDictionary -> Book -Book *book = [Book mj_objectWithKeyValues:dict]; - -// printing -NSLog(@"name=%@, publisher=%@, publishedTime=%@", book.name, book.publisher, book.publishedTime); -``` - -### More use cases【更多用法】 -- Please reference to `NSObject+MJKeyValue.h` and `NSObject+MJCoding.h` - - -## 期待 -* 如果在使用过程中遇到BUG,希望你能Issues我,谢谢(或者尝试下载最新的框架代码看看BUG修复没有) -* 如果在使用过程中发现功能不够用,希望你能Issues我,我非常想为这个框架增加更多好用的功能,谢谢 -* 如果你想为MJExtension输出代码,请拼命Pull Requests我 diff --git a/Pods/MJRefresh/LICENSE b/Pods/MJRefresh/LICENSE deleted file mode 100644 index 11bf234..0000000 --- a/Pods/MJRefresh/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2013-2015 MJRefresh (https://github.com/CoderMJLee/MJRefresh) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h deleted file mode 100644 index 5aac4f6..0000000 --- a/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// MJRefreshAutoFooter.h -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshFooter.h" - -@interface MJRefreshAutoFooter : MJRefreshFooter -/** 是否自动刷新(默认为YES) */ -@property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh; - -/** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */ -@property (assign, nonatomic) CGFloat appearencePercentTriggerAutoRefresh MJRefreshDeprecated("请使用automaticallyChangeAlpha属性"); - -/** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */ -@property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent; -@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m deleted file mode 100644 index fd1687f..0000000 --- a/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m +++ /dev/null @@ -1,133 +0,0 @@ -// -// MJRefreshAutoFooter.m -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshAutoFooter.h" - -@interface MJRefreshAutoFooter() -@end - -@implementation MJRefreshAutoFooter - -#pragma mark - 初始化 -- (void)willMoveToSuperview:(UIView *)newSuperview -{ - [super willMoveToSuperview:newSuperview]; - - if (newSuperview) { // 新的父控件 - if (self.hidden == NO) { - self.scrollView.mj_insetB += self.mj_h; - } - - // 设置位置 - self.mj_y = _scrollView.mj_contentH; - } else { // 被移除了 - if (self.hidden == NO) { - self.scrollView.mj_insetB -= self.mj_h; - } - } -} - -#pragma mark - 过期方法 -- (void)setAppearencePercentTriggerAutoRefresh:(CGFloat)appearencePercentTriggerAutoRefresh -{ - self.triggerAutomaticallyRefreshPercent = appearencePercentTriggerAutoRefresh; -} - -- (CGFloat)appearencePercentTriggerAutoRefresh -{ - return self.triggerAutomaticallyRefreshPercent; -} - -#pragma mark - 实现父类的方法 -- (void)prepare -{ - [super prepare]; - - // 默认底部控件100%出现时才会自动刷新 - self.triggerAutomaticallyRefreshPercent = 1.0; - - // 设置为默认状态 - self.automaticallyRefresh = YES; -} - -- (void)scrollViewContentSizeDidChange:(NSDictionary *)change -{ - [super scrollViewContentSizeDidChange:change]; - - // 设置位置 - self.mj_y = self.scrollView.mj_contentH; -} - -- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change -{ - [super scrollViewContentOffsetDidChange:change]; - - if (self.state != MJRefreshStateIdle || !self.automaticallyRefresh || self.mj_y == 0) return; - - if (_scrollView.mj_insetT + _scrollView.mj_contentH > _scrollView.mj_h) { // 内容超过一个屏幕 - // 这里的_scrollView.mj_contentH替换掉self.mj_y更为合理 - if (_scrollView.mj_offsetY >= _scrollView.mj_contentH - _scrollView.mj_h + self.mj_h * self.triggerAutomaticallyRefreshPercent + _scrollView.mj_insetB - self.mj_h) { - // 防止手松开时连续调用 - CGPoint old = [change[@"old"] CGPointValue]; - CGPoint new = [change[@"new"] CGPointValue]; - if (new.y <= old.y) return; - - // 当底部刷新控件完全出现时,才刷新 - [self beginRefreshing]; - } - } -} - -- (void)scrollViewPanStateDidChange:(NSDictionary *)change -{ - [super scrollViewPanStateDidChange:change]; - - if (self.state != MJRefreshStateIdle) return; - - if (_scrollView.panGestureRecognizer.state == UIGestureRecognizerStateEnded) {// 手松开 - if (_scrollView.mj_insetT + _scrollView.mj_contentH <= _scrollView.mj_h) { // 不够一个屏幕 - if (_scrollView.mj_offsetY >= - _scrollView.mj_insetT) { // 向上拽 - [self beginRefreshing]; - } - } else { // 超出一个屏幕 - if (_scrollView.mj_offsetY >= _scrollView.mj_contentH + _scrollView.mj_insetB - _scrollView.mj_h) { - [self beginRefreshing]; - } - } - } -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - if (state == MJRefreshStateRefreshing) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self executeRefreshingCallback]; - }); - } -} - -- (void)setHidden:(BOOL)hidden -{ - BOOL lastHidden = self.isHidden; - - [super setHidden:hidden]; - - if (!lastHidden && hidden) { - self.state = MJRefreshStateIdle; - - self.scrollView.mj_insetB -= self.mj_h; - } else if (lastHidden && !hidden) { - self.scrollView.mj_insetB += self.mj_h; - - // 设置位置 - self.mj_y = _scrollView.mj_contentH; - } -} -@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h deleted file mode 100644 index 347083c..0000000 --- a/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// MJRefreshBackFooter.h -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshFooter.h" - -@interface MJRefreshBackFooter : MJRefreshFooter - -@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m deleted file mode 100644 index 0ffc5e9..0000000 --- a/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m +++ /dev/null @@ -1,166 +0,0 @@ -// -// MJRefreshBackFooter.m -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshBackFooter.h" - -@interface MJRefreshBackFooter() -@property (assign, nonatomic) NSInteger lastRefreshCount; -@property (assign, nonatomic) CGFloat lastBottomDelta; -@end - -@implementation MJRefreshBackFooter - -#pragma mark - 初始化 -- (void)willMoveToSuperview:(UIView *)newSuperview -{ - [super willMoveToSuperview:newSuperview]; - - [self scrollViewContentSizeDidChange:nil]; -} - -#pragma mark - 实现父类的方法 -- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change -{ - [super scrollViewContentOffsetDidChange:change]; - - // 如果正在刷新,直接返回 - if (self.state == MJRefreshStateRefreshing) return; - - _scrollViewOriginalInset = self.scrollView.contentInset; - - // 当前的contentOffset - CGFloat currentOffsetY = self.scrollView.mj_offsetY; - // 尾部控件刚好出现的offsetY - CGFloat happenOffsetY = [self happenOffsetY]; - // 如果是向下滚动到看不见尾部控件,直接返回 - if (currentOffsetY <= happenOffsetY) return; - - CGFloat pullingPercent = (currentOffsetY - happenOffsetY) / self.mj_h; - - // 如果已全部加载,仅设置pullingPercent,然后返回 - if (self.state == MJRefreshStateNoMoreData) { - self.pullingPercent = pullingPercent; - return; - } - - if (self.scrollView.isDragging) { - self.pullingPercent = pullingPercent; - // 普通 和 即将刷新 的临界点 - CGFloat normal2pullingOffsetY = happenOffsetY + self.mj_h; - - if (self.state == MJRefreshStateIdle && currentOffsetY > normal2pullingOffsetY) { - // 转为即将刷新状态 - self.state = MJRefreshStatePulling; - } else if (self.state == MJRefreshStatePulling && currentOffsetY <= normal2pullingOffsetY) { - // 转为普通状态 - self.state = MJRefreshStateIdle; - } - } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开 - // 开始刷新 - [self beginRefreshing]; - } else if (pullingPercent < 1) { - self.pullingPercent = pullingPercent; - } -} - -- (void)scrollViewContentSizeDidChange:(NSDictionary *)change -{ - [super scrollViewContentSizeDidChange:change]; - - // 内容的高度 - CGFloat contentHeight = self.scrollView.mj_contentH + self.ignoredScrollViewContentInsetBottom; - // 表格的高度 - CGFloat scrollHeight = self.scrollView.mj_h - self.scrollViewOriginalInset.top - self.scrollViewOriginalInset.bottom + self.ignoredScrollViewContentInsetBottom; - // 设置位置和尺寸 - self.mj_y = MAX(contentHeight, scrollHeight); -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - // 根据状态来设置属性 - if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { - // 刷新完毕 - if (MJRefreshStateRefreshing == oldState) { - [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ - self.scrollView.mj_insetB -= self.lastBottomDelta; - - // 自动调整透明度 - if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0; - } completion:^(BOOL finished) { - self.pullingPercent = 0.0; - }]; - } - - CGFloat deltaH = [self heightForContentBreakView]; - // 刚刷新完毕 - if (MJRefreshStateRefreshing == oldState && deltaH > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) { - self.scrollView.mj_offsetY = self.scrollView.mj_offsetY; - } - } else if (state == MJRefreshStateRefreshing) { - // 记录刷新前的数量 - self.lastRefreshCount = self.scrollView.mj_totalDataCount; - - [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ - CGFloat bottom = self.mj_h + self.scrollViewOriginalInset.bottom; - CGFloat deltaH = [self heightForContentBreakView]; - if (deltaH < 0) { // 如果内容高度小于view的高度 - bottom -= deltaH; - } - self.lastBottomDelta = bottom - self.scrollView.mj_insetB; - self.scrollView.mj_insetB = bottom; - self.scrollView.mj_offsetY = [self happenOffsetY] + self.mj_h; - } completion:^(BOOL finished) { - [self executeRefreshingCallback]; - }]; - } -} - -#pragma mark - 公共方法 -- (void)endRefreshing -{ - if ([self.scrollView isKindOfClass:[UICollectionView class]]) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [super endRefreshing]; - }); - } else { - [super endRefreshing]; - } -} - -- (void)noticeNoMoreData -{ - if ([self.scrollView isKindOfClass:[UICollectionView class]]) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [super noticeNoMoreData]; - }); - } else { - [super noticeNoMoreData]; - } -} - -#pragma mark - 私有方法 -#pragma mark 获得scrollView的内容 超出 view 的高度 -- (CGFloat)heightForContentBreakView -{ - CGFloat h = self.scrollView.frame.size.height - self.scrollViewOriginalInset.bottom - self.scrollViewOriginalInset.top; - return self.scrollView.contentSize.height - h; -} - -#pragma mark 刚好看到上拉刷新控件时的contentOffset.y -- (CGFloat)happenOffsetY -{ - CGFloat deltaH = [self heightForContentBreakView]; - if (deltaH > 0) { - return deltaH - self.scrollViewOriginalInset.top; - } else { - return - self.scrollViewOriginalInset.top; - } -} -@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h deleted file mode 100644 index 5f59361..0000000 --- a/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h +++ /dev/null @@ -1,93 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// MJRefreshComponent.h -// MJRefreshExample -// -// Created by MJ Lee on 15/3/4. -// Copyright (c) 2015年 小码哥. All rights reserved. -// 刷新控件的基类 - -#import -#import "MJRefreshConst.h" -#import "UIView+MJExtension.h" -#import "UIScrollView+MJExtension.h" -#import "UIScrollView+MJRefresh.h" - -/** 刷新控件的状态 */ -typedef NS_ENUM(NSInteger, MJRefreshState) { - /** 普通闲置状态 */ - MJRefreshStateIdle = 1, - /** 松开就可以进行刷新的状态 */ - MJRefreshStatePulling, - /** 正在刷新中的状态 */ - MJRefreshStateRefreshing, - /** 即将刷新的状态 */ - MJRefreshStateWillRefresh, - /** 所有数据加载完毕,没有更多的数据了 */ - MJRefreshStateNoMoreData -}; - -/** 进入刷新状态的回调 */ -typedef void (^MJRefreshComponentRefreshingBlock)(); - -/** 刷新控件的基类 */ -@interface MJRefreshComponent : UIView -{ - /** 记录scrollView刚开始的inset */ - UIEdgeInsets _scrollViewOriginalInset; - /** 父控件 */ - __weak UIScrollView *_scrollView; -} -#pragma mark - 刷新回调 -/** 正在刷新的回调 */ -@property (copy, nonatomic) MJRefreshComponentRefreshingBlock refreshingBlock; -/** 设置回调对象和回调方法 */ -- (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action; -/** 回调对象 */ -@property (weak, nonatomic) id refreshingTarget; -/** 回调方法 */ -@property (assign, nonatomic) SEL refreshingAction; -/** 触发回调(交给子类去调用) */ -- (void)executeRefreshingCallback; - -#pragma mark - 刷新状态控制 -/** 进入刷新状态 */ -- (void)beginRefreshing; -/** 结束刷新状态 */ -- (void)endRefreshing; -/** 是否正在刷新 */ -- (BOOL)isRefreshing; -/** 刷新状态 一般交给子类内部实现 */ -@property (assign, nonatomic) MJRefreshState state; - -#pragma mark - 交给子类去访问 -/** 记录scrollView刚开始的inset */ -@property (assign, nonatomic, readonly) UIEdgeInsets scrollViewOriginalInset; -/** 父控件 */ -@property (weak, nonatomic, readonly) UIScrollView *scrollView; - -#pragma mark - 交给子类们去实现 -/** 初始化 */ -- (void)prepare NS_REQUIRES_SUPER; -/** 摆放子控件frame */ -- (void)placeSubviews NS_REQUIRES_SUPER; -/** 当scrollView的contentOffset发生改变的时候调用 */ -- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change NS_REQUIRES_SUPER; -/** 当scrollView的contentSize发生改变的时候调用 */ -- (void)scrollViewContentSizeDidChange:(NSDictionary *)change NS_REQUIRES_SUPER; -/** 当scrollView的拖拽状态发生改变的时候调用 */ -- (void)scrollViewPanStateDidChange:(NSDictionary *)change NS_REQUIRES_SUPER; - - -#pragma mark - 其他 -/** 拉拽的百分比(交给子类重写) */ -@property (assign, nonatomic) CGFloat pullingPercent; -/** 根据拖拽比例自动切换透明度 */ -@property (assign, nonatomic, getter=isAutoChangeAlpha) BOOL autoChangeAlpha MJRefreshDeprecated("请使用automaticallyChangeAlpha属性"); -/** 根据拖拽比例自动切换透明度 */ -@property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha; -@end - -@interface UILabel(MJRefresh) -+ (instancetype)label; -@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m deleted file mode 100644 index 1b6ea27..0000000 --- a/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m +++ /dev/null @@ -1,226 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// MJRefreshComponent.m -// MJRefreshExample -// -// Created by MJ Lee on 15/3/4. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshComponent.h" -#import "MJRefreshConst.h" -#import "UIView+MJExtension.h" -#import "UIScrollView+MJRefresh.h" - -@interface MJRefreshComponent() -@property (strong, nonatomic) UIPanGestureRecognizer *pan; -@end - -@implementation MJRefreshComponent -#pragma mark - 初始化 -- (instancetype)initWithFrame:(CGRect)frame -{ - if (self = [super initWithFrame:frame]) { - // 准备工作 - [self prepare]; - - // 默认是普通状态 - self.state = MJRefreshStateIdle; - } - return self; -} - -- (void)prepare -{ - // 基本属性 - self.autoresizingMask = UIViewAutoresizingFlexibleWidth; - self.backgroundColor = [UIColor clearColor]; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - - [self placeSubviews]; -} - -- (void)placeSubviews{} - -- (void)willMoveToSuperview:(UIView *)newSuperview -{ - [super willMoveToSuperview:newSuperview]; - - // 如果不是UIScrollView,不做任何事情 - if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) return; - - // 旧的父控件移除监听 - [self removeObservers]; - - if (newSuperview) { // 新的父控件 - // 设置宽度 - self.mj_w = newSuperview.mj_w; - // 设置位置 - self.mj_x = 0; - - // 记录UIScrollView - _scrollView = (UIScrollView *)newSuperview; - // 设置永远支持垂直弹簧效果 - _scrollView.alwaysBounceVertical = YES; - // 记录UIScrollView最开始的contentInset - _scrollViewOriginalInset = _scrollView.contentInset; - - // 添加监听 - [self addObservers]; - } -} - -- (void)drawRect:(CGRect)rect -{ - [super drawRect:rect]; - - if (self.state == MJRefreshStateWillRefresh) { - // 预防view还没显示出来就调用了beginRefreshing - self.state = MJRefreshStateRefreshing; - } -} - -#pragma mark - KVO监听 -- (void)addObservers -{ - NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld; - [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentOffset options:options context:nil]; - [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil]; - self.pan = self.scrollView.panGestureRecognizer; - [self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil]; -} - -- (void)removeObservers -{ - [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentOffset]; - [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentSize];; - [self.pan removeObserver:self forKeyPath:MJRefreshKeyPathPanState]; - self.pan = nil; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - // 遇到这些情况就直接返回 - if (!self.userInteractionEnabled) return; - - // 这个就算看不见也需要处理 - if ([keyPath isEqualToString:MJRefreshKeyPathContentSize]) { - [self scrollViewContentSizeDidChange:change]; - } - - // 看不见 - if (self.hidden) return; - if ([keyPath isEqualToString:MJRefreshKeyPathContentOffset]) { - [self scrollViewContentOffsetDidChange:change]; - } else if ([keyPath isEqualToString:MJRefreshKeyPathPanState]) { - [self scrollViewPanStateDidChange:change]; - } -} - -- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{} -- (void)scrollViewContentSizeDidChange:(NSDictionary *)change{} -- (void)scrollViewPanStateDidChange:(NSDictionary *)change{} - -#pragma mark - 公共方法 -#pragma mark 设置回调对象和回调方法 -- (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action -{ - self.refreshingTarget = target; - self.refreshingAction = action; -} - -#pragma mark 进入刷新状态 -- (void)beginRefreshing -{ - [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ - self.alpha = 1.0; - }]; - self.pullingPercent = 1.0; - // 只要正在刷新,就完全显示 - if (self.window) { - self.state = MJRefreshStateRefreshing; - } else { - self.state = MJRefreshStateWillRefresh; - // 刷新(预防从另一个控制器回到这个控制器的情况,回来要重新刷新一下) - [self setNeedsDisplay]; - } -} - -#pragma mark 结束刷新状态 -- (void)endRefreshing -{ - self.state = MJRefreshStateIdle; -} - -#pragma mark 是否正在刷新 -- (BOOL)isRefreshing -{ - return self.state == MJRefreshStateRefreshing || self.state == MJRefreshStateWillRefresh; -} - -#pragma mark 自动切换透明度 -- (void)setAutoChangeAlpha:(BOOL)autoChangeAlpha -{ - self.automaticallyChangeAlpha = autoChangeAlpha; -} - -- (BOOL)isAutoChangeAlpha -{ - return self.isAutomaticallyChangeAlpha; -} - -- (void)setAutomaticallyChangeAlpha:(BOOL)automaticallyChangeAlpha -{ - _automaticallyChangeAlpha = automaticallyChangeAlpha; - - if (self.isRefreshing) return; - - if (automaticallyChangeAlpha) { - self.alpha = self.pullingPercent; - } else { - self.alpha = 1.0; - } -} - -#pragma mark 根据拖拽进度设置透明度 -- (void)setPullingPercent:(CGFloat)pullingPercent -{ - _pullingPercent = pullingPercent; - - if (self.isRefreshing) return; - - if (self.isAutomaticallyChangeAlpha) { - self.alpha = pullingPercent; - } -} - -#pragma mark - 内部方法 -- (void)executeRefreshingCallback -{ - dispatch_async(dispatch_get_main_queue(), ^{ - if (self.refreshingBlock) { - self.refreshingBlock(); - } - if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) { - MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self); - } - }); -} -@end - -@implementation UILabel(MJRefresh) -+ (instancetype)label -{ - UILabel *label = [[self alloc] init]; - label.font = MJRefreshLabelFont; - label.textColor = MJRefreshLabelTextColor; - label.autoresizingMask = UIViewAutoresizingFlexibleWidth; - label.textAlignment = NSTextAlignmentCenter; - label.backgroundColor = [UIColor clearColor]; - return label; -} -@end \ No newline at end of file diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h deleted file mode 100644 index 22d23e5..0000000 --- a/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h +++ /dev/null @@ -1,30 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// MJRefreshFooter.h -// MJRefreshExample -// -// Created by MJ Lee on 15/3/5. -// Copyright (c) 2015年 小码哥. All rights reserved. -// 上拉刷新控件 - -#import "MJRefreshComponent.h" - -@interface MJRefreshFooter : MJRefreshComponent -/** 创建footer */ -+ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; -/** 创建footer */ -+ (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; - -/** 提示没有更多的数据 */ -- (void)endRefreshingWithNoMoreData; -- (void)noticeNoMoreData MJRefreshDeprecated("使用endRefreshingWithNoMoreData"); - -/** 重置没有更多的数据(消除没有更多数据的状态) */ -- (void)resetNoMoreData; - -/** 忽略多少scrollView的contentInset的bottom */ -@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom; - -/** 自动根据有无数据来显示和隐藏(有数据就显示,没有数据隐藏。默认是NO) */ -@property (assign, nonatomic, getter=isAutomaticallyHidden) BOOL automaticallyHidden; -@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m deleted file mode 100644 index 69f65e7..0000000 --- a/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m +++ /dev/null @@ -1,74 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// MJRefreshFooter.m -// MJRefreshExample -// -// Created by MJ Lee on 15/3/5. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshFooter.h" - -@interface MJRefreshFooter() - -@end - -@implementation MJRefreshFooter -#pragma mark - 构造方法 -+ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock -{ - MJRefreshFooter *cmp = [[self alloc] init]; - cmp.refreshingBlock = refreshingBlock; - return cmp; -} -+ (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action -{ - MJRefreshFooter *cmp = [[self alloc] init]; - [cmp setRefreshingTarget:target refreshingAction:action]; - return cmp; -} - -#pragma mark - 重写父类的方法 -- (void)prepare -{ - [super prepare]; - - // 设置自己的高度 - self.mj_h = MJRefreshFooterHeight; - - // 默认不会自动隐藏 - self.automaticallyHidden = NO; -} - -- (void)willMoveToSuperview:(UIView *)newSuperview -{ - [super willMoveToSuperview:newSuperview]; - - if (newSuperview) { - // 监听scrollView数据的变化 - if ([self.scrollView isKindOfClass:[UITableView class]] || [self.scrollView isKindOfClass:[UICollectionView class]]) { - [self.scrollView setMj_reloadDataBlock:^(NSInteger totalDataCount) { - if (self.isAutomaticallyHidden) { - self.hidden = (totalDataCount == 0); - } - }]; - } - } -} - -#pragma mark - 公共方法 -- (void)endRefreshingWithNoMoreData -{ - self.state = MJRefreshStateNoMoreData; -} - -- (void)noticeNoMoreData -{ - [self endRefreshingWithNoMoreData]; -} - -- (void)resetNoMoreData -{ - self.state = MJRefreshStateIdle; -} -@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h deleted file mode 100644 index 0816024..0000000 --- a/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h +++ /dev/null @@ -1,25 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// MJRefreshHeader.h -// MJRefreshExample -// -// Created by MJ Lee on 15/3/4. -// Copyright (c) 2015年 小码哥. All rights reserved. -// 下拉刷新控件:负责监控用户下拉的状态 - -#import "MJRefreshComponent.h" - -@interface MJRefreshHeader : MJRefreshComponent -/** 创建header */ -+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; -/** 创建header */ -+ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; - -/** 这个key用来存储上一次下拉刷新成功的时间 */ -@property (copy, nonatomic) NSString *lastUpdatedTimeKey; -/** 上一次下拉刷新成功的时间 */ -@property (strong, nonatomic, readonly) NSDate *lastUpdatedTime; - -/** 忽略多少scrollView的contentInset的top */ -@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop; -@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m deleted file mode 100644 index 852f3c7..0000000 --- a/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m +++ /dev/null @@ -1,159 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// MJRefreshHeader.m -// MJRefreshExample -// -// Created by MJ Lee on 15/3/4. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshHeader.h" - -@interface MJRefreshHeader() -@property (assign, nonatomic) CGFloat insetTDelta; -@end - -@implementation MJRefreshHeader -#pragma mark - 构造方法 -+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock -{ - MJRefreshHeader *cmp = [[self alloc] init]; - cmp.refreshingBlock = refreshingBlock; - return cmp; -} -+ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action -{ - MJRefreshHeader *cmp = [[self alloc] init]; - [cmp setRefreshingTarget:target refreshingAction:action]; - return cmp; -} - -#pragma mark - 覆盖父类的方法 -- (void)prepare -{ - [super prepare]; - - // 设置key - self.lastUpdatedTimeKey = MJRefreshHeaderLastUpdatedTimeKey; - - // 设置高度 - self.mj_h = MJRefreshHeaderHeight; -} - -- (void)placeSubviews -{ - [super placeSubviews]; - - // 设置y值(当自己的高度发生改变了,肯定要重新调整Y值,所以放到placeSubviews方法中设置y值) - self.mj_y = - self.mj_h - self.ignoredScrollViewContentInsetTop; -} - -- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change -{ - [super scrollViewContentOffsetDidChange:change]; - - // 在刷新的refreshing状态 - if (self.state == MJRefreshStateRefreshing) { - if (self.window == nil) return; - - // sectionheader停留解决 - CGFloat insetT = - self.scrollView.mj_offsetY > _scrollViewOriginalInset.top ? - self.scrollView.mj_offsetY : _scrollViewOriginalInset.top; - insetT = insetT > self.mj_h + _scrollViewOriginalInset.top ? self.mj_h + _scrollViewOriginalInset.top : insetT; - self.scrollView.mj_insetT = insetT; - - self.insetTDelta = _scrollViewOriginalInset.top - insetT; - return; - } - - // 跳转到下一个控制器时,contentInset可能会变 - _scrollViewOriginalInset = self.scrollView.contentInset; - - // 当前的contentOffset - CGFloat offsetY = self.scrollView.mj_offsetY; - // 头部控件刚好出现的offsetY - CGFloat happenOffsetY = - self.scrollViewOriginalInset.top; - - // 如果是向上滚动到看不见头部控件,直接返回 - // >= -> > - if (offsetY > happenOffsetY) return; - - // 普通 和 即将刷新 的临界点 - CGFloat normal2pullingOffsetY = happenOffsetY - self.mj_h; - CGFloat pullingPercent = (happenOffsetY - offsetY) / self.mj_h; - - if (self.scrollView.isDragging) { // 如果正在拖拽 - self.pullingPercent = pullingPercent; - if (self.state == MJRefreshStateIdle && offsetY < normal2pullingOffsetY) { - // 转为即将刷新状态 - self.state = MJRefreshStatePulling; - } else if (self.state == MJRefreshStatePulling && offsetY >= normal2pullingOffsetY) { - // 转为普通状态 - self.state = MJRefreshStateIdle; - } - } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开 - // 开始刷新 - [self beginRefreshing]; - } else if (pullingPercent < 1) { - self.pullingPercent = pullingPercent; - } -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - // 根据状态做事情 - if (state == MJRefreshStateIdle) { - if (oldState != MJRefreshStateRefreshing) return; - - // 保存刷新时间 - [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:self.lastUpdatedTimeKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; - - // 恢复inset和offset - [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ - self.scrollView.mj_insetT += self.insetTDelta; - - // 自动调整透明度 - if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0; - } completion:^(BOOL finished) { - self.pullingPercent = 0.0; - }]; - } else if (state == MJRefreshStateRefreshing) { - [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ - // 增加滚动区域 - CGFloat top = self.scrollViewOriginalInset.top + self.mj_h; - self.scrollView.mj_insetT = top; - - // 设置滚动位置 - self.scrollView.mj_offsetY = - top; - } completion:^(BOOL finished) { - [self executeRefreshingCallback]; - }]; - } -} - -- (void)drawRect:(CGRect)rect -{ - [super drawRect:rect]; - - -} - -#pragma mark - 公共方法 -- (void)endRefreshing -{ - if ([self.scrollView isKindOfClass:[UICollectionView class]]) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [super endRefreshing]; - }); - } else { - [super endRefreshing]; - } -} - -- (NSDate *)lastUpdatedTime -{ - return [[NSUserDefaults standardUserDefaults] objectForKey:self.lastUpdatedTimeKey]; -} -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h deleted file mode 100644 index 6a127e6..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// MJRefreshAutoGifFooter.h -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshAutoStateFooter.h" - -@interface MJRefreshAutoGifFooter : MJRefreshAutoStateFooter -@property (weak, nonatomic, readonly) UIImageView *gifView; - -/** 设置state状态下的动画图片images 动画持续时间duration*/ -- (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; -- (void)setImages:(NSArray *)images forState:(MJRefreshState)state; -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m deleted file mode 100644 index 9e68606..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m +++ /dev/null @@ -1,108 +0,0 @@ -// -// MJRefreshAutoGifFooter.m -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshAutoGifFooter.h" - -@interface MJRefreshAutoGifFooter() -{ - __unsafe_unretained UIImageView *_gifView; -} -/** 所有状态对应的动画图片 */ -@property (strong, nonatomic) NSMutableDictionary *stateImages; -/** 所有状态对应的动画时间 */ -@property (strong, nonatomic) NSMutableDictionary *stateDurations; -@end - -@implementation MJRefreshAutoGifFooter -#pragma mark - 懒加载 -- (UIImageView *)gifView -{ - if (!_gifView) { - UIImageView *gifView = [[UIImageView alloc] init]; - [self addSubview:_gifView = gifView]; - } - return _gifView; -} - -- (NSMutableDictionary *)stateImages -{ - if (!_stateImages) { - self.stateImages = [NSMutableDictionary dictionary]; - } - return _stateImages; -} - -- (NSMutableDictionary *)stateDurations -{ - if (!_stateDurations) { - self.stateDurations = [NSMutableDictionary dictionary]; - } - return _stateDurations; -} - -#pragma mark - 公共方法 -- (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state -{ - if (images == nil) return; - - self.stateImages[@(state)] = images; - self.stateDurations[@(state)] = @(duration); - - /* 根据图片设置控件的高度 */ - UIImage *image = [images firstObject]; - if (image.size.height > self.mj_h) { - self.mj_h = image.size.height; - } -} - -- (void)setImages:(NSArray *)images forState:(MJRefreshState)state -{ - [self setImages:images duration:images.count * 0.1 forState:state]; -} - -#pragma mark - 实现父类的方法 -- (void)placeSubviews -{ - [super placeSubviews]; - - if (self.gifView.constraints.count) return; - - self.gifView.frame = self.bounds; - if (self.isRefreshingTitleHidden) { - self.gifView.contentMode = UIViewContentModeCenter; - } else { - self.gifView.contentMode = UIViewContentModeRight; - self.gifView.mj_w = self.mj_w * 0.5 - 90; - } -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - // 根据状态做事情 - if (state == MJRefreshStateRefreshing) { - NSArray *images = self.stateImages[@(state)]; - if (images.count == 0) return; - [self.gifView stopAnimating]; - - self.gifView.hidden = NO; - if (images.count == 1) { // 单张图片 - self.gifView.image = [images lastObject]; - } else { // 多张图片 - self.gifView.animationImages = images; - self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; - [self.gifView startAnimating]; - } - } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { - [self.gifView stopAnimating]; - self.gifView.hidden = YES; - } -} -@end - diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h deleted file mode 100644 index 5549cff..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// MJRefreshAutoNormalFooter.h -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshAutoStateFooter.h" - -@interface MJRefreshAutoNormalFooter : MJRefreshAutoStateFooter -/** 菊花的样式 */ -@property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle; -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m deleted file mode 100644 index c14bdf8..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m +++ /dev/null @@ -1,69 +0,0 @@ -// -// MJRefreshAutoNormalFooter.m -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshAutoNormalFooter.h" - -@interface MJRefreshAutoNormalFooter() -@property (weak, nonatomic) UIActivityIndicatorView *loadingView; -@end - -@implementation MJRefreshAutoNormalFooter -#pragma mark - 懒加载子控件 -- (UIActivityIndicatorView *)loadingView -{ - if (!_loadingView) { - UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle]; - loadingView.hidesWhenStopped = YES; - [self addSubview:_loadingView = loadingView]; - } - return _loadingView; -} - -- (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle -{ - _activityIndicatorViewStyle = activityIndicatorViewStyle; - - self.loadingView = nil; - [self setNeedsLayout]; -} -#pragma makr - 重写父类的方法 -- (void)prepare -{ - [super prepare]; - - self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; -} - -- (void)placeSubviews -{ - [super placeSubviews]; - - if (self.loadingView.constraints.count) return; - - // 圈圈 - CGFloat loadingCenterX = self.mj_w * 0.5; - if (!self.isRefreshingTitleHidden) { - loadingCenterX -= 100; - } - CGFloat loadingCenterY = self.mj_h * 0.5; - self.loadingView.center = CGPointMake(loadingCenterX, loadingCenterY); -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - // 根据状态做事情 - if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { - [self.loadingView stopAnimating]; - } else if (state == MJRefreshStateRefreshing) { - [self.loadingView startAnimating]; - } -} - -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h deleted file mode 100644 index 956188b..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// MJRefreshAutoStateFooter.h -// MJRefreshExample -// -// Created by MJ Lee on 15/6/13. -// Copyright © 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshAutoFooter.h" - -@interface MJRefreshAutoStateFooter : MJRefreshAutoFooter -/** 显示刷新状态的label */ -@property (weak, nonatomic, readonly) UILabel *stateLabel; - -/** 设置state状态下的文字 */ -- (void)setTitle:(NSString *)title forState:(MJRefreshState)state; - -/** 隐藏刷新状态的文字 */ -@property (assign, nonatomic, getter=isRefreshingTitleHidden) BOOL refreshingTitleHidden; -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m deleted file mode 100644 index 9135bf1..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m +++ /dev/null @@ -1,89 +0,0 @@ -// -// MJRefreshAutoStateFooter.m -// MJRefreshExample -// -// Created by MJ Lee on 15/6/13. -// Copyright © 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshAutoStateFooter.h" - -@interface MJRefreshAutoStateFooter() -{ - /** 显示刷新状态的label */ - __unsafe_unretained UILabel *_stateLabel; -} -/** 所有状态对应的文字 */ -@property (strong, nonatomic) NSMutableDictionary *stateTitles; -@end - -@implementation MJRefreshAutoStateFooter -#pragma mark - 懒加载 -- (NSMutableDictionary *)stateTitles -{ - if (!_stateTitles) { - self.stateTitles = [NSMutableDictionary dictionary]; - } - return _stateTitles; -} - -- (UILabel *)stateLabel -{ - if (!_stateLabel) { - [self addSubview:_stateLabel = [UILabel label]]; - } - return _stateLabel; -} - -#pragma mark - 公共方法 -- (void)setTitle:(NSString *)title forState:(MJRefreshState)state -{ - if (title == nil) return; - self.stateTitles[@(state)] = title; - self.stateLabel.text = self.stateTitles[@(self.state)]; -} - -#pragma mark - 私有方法 -- (void)stateLabelClick -{ - if (self.state == MJRefreshStateIdle) { - [self beginRefreshing]; - } -} - -#pragma mark - 重写父类的方法 -- (void)prepare -{ - [super prepare]; - - // 初始化文字 - [self setTitle:MJRefreshAutoFooterIdleText forState:MJRefreshStateIdle]; - [self setTitle:MJRefreshAutoFooterRefreshingText forState:MJRefreshStateRefreshing]; - [self setTitle:MJRefreshAutoFooterNoMoreDataText forState:MJRefreshStateNoMoreData]; - - // 监听label - self.stateLabel.userInteractionEnabled = YES; - [self.stateLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(stateLabelClick)]]; -} - -- (void)placeSubviews -{ - [super placeSubviews]; - - if (self.stateLabel.constraints.count) return; - - // 状态标签 - self.stateLabel.frame = self.bounds; -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - if (self.isRefreshingTitleHidden && state == MJRefreshStateRefreshing) { - self.stateLabel.text = nil; - } else { - self.stateLabel.text = self.stateTitles[@(state)]; - } -} -@end \ No newline at end of file diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h deleted file mode 100644 index b29af86..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// MJRefreshBackGifFooter.h -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshBackStateFooter.h" - -@interface MJRefreshBackGifFooter : MJRefreshBackStateFooter -@property (weak, nonatomic, readonly) UIImageView *gifView; - -/** 设置state状态下的动画图片images 动画持续时间duration*/ -- (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; -- (void)setImages:(NSArray *)images forState:(MJRefreshState)state; -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m deleted file mode 100644 index ae233e0..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m +++ /dev/null @@ -1,119 +0,0 @@ -// -// MJRefreshBackGifFooter.m -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshBackGifFooter.h" - -@interface MJRefreshBackGifFooter() -{ - __unsafe_unretained UIImageView *_gifView; -} -/** 所有状态对应的动画图片 */ -@property (strong, nonatomic) NSMutableDictionary *stateImages; -/** 所有状态对应的动画时间 */ -@property (strong, nonatomic) NSMutableDictionary *stateDurations; -@end - -@implementation MJRefreshBackGifFooter -#pragma mark - 懒加载 -- (UIImageView *)gifView -{ - if (!_gifView) { - UIImageView *gifView = [[UIImageView alloc] init]; - [self addSubview:_gifView = gifView]; - } - return _gifView; -} - -- (NSMutableDictionary *)stateImages -{ - if (!_stateImages) { - self.stateImages = [NSMutableDictionary dictionary]; - } - return _stateImages; -} - -- (NSMutableDictionary *)stateDurations -{ - if (!_stateDurations) { - self.stateDurations = [NSMutableDictionary dictionary]; - } - return _stateDurations; -} - -#pragma mark - 公共方法 -- (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state -{ - if (images == nil) return; - - self.stateImages[@(state)] = images; - self.stateDurations[@(state)] = @(duration); - - /* 根据图片设置控件的高度 */ - UIImage *image = [images firstObject]; - if (image.size.height > self.mj_h) { - self.mj_h = image.size.height; - } -} - -- (void)setImages:(NSArray *)images forState:(MJRefreshState)state -{ - [self setImages:images duration:images.count * 0.1 forState:state]; -} - -#pragma mark - 实现父类的方法 -- (void)setPullingPercent:(CGFloat)pullingPercent -{ - [super setPullingPercent:pullingPercent]; - NSArray *images = self.stateImages[@(MJRefreshStateIdle)]; - if (self.state != MJRefreshStateIdle || images.count == 0) return; - [self.gifView stopAnimating]; - NSUInteger index = images.count * pullingPercent; - if (index >= images.count) index = images.count - 1; - self.gifView.image = images[index]; -} - -- (void)placeSubviews -{ - [super placeSubviews]; - - if (self.gifView.constraints.count) return; - - self.gifView.frame = self.bounds; - if (self.stateLabel.hidden) { - self.gifView.contentMode = UIViewContentModeCenter; - } else { - self.gifView.contentMode = UIViewContentModeRight; - self.gifView.mj_w = self.mj_w * 0.5 - 90; - } -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - // 根据状态做事情 - if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) { - NSArray *images = self.stateImages[@(state)]; - if (images.count == 0) return; - - self.gifView.hidden = NO; - [self.gifView stopAnimating]; - if (images.count == 1) { // 单张图片 - self.gifView.image = [images lastObject]; - } else { // 多张图片 - self.gifView.animationImages = images; - self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; - [self.gifView startAnimating]; - } - } else if (state == MJRefreshStateIdle) { - self.gifView.hidden = NO; - } else if (state == MJRefreshStateNoMoreData) { - self.gifView.hidden = YES; - } -} -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h deleted file mode 100644 index 90e8b77..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// MJRefreshBackNormalFooter.h -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshBackStateFooter.h" - -@interface MJRefreshBackNormalFooter : MJRefreshBackStateFooter -@property (weak, nonatomic, readonly) UIImageView *arrowView; -/** 菊花的样式 */ -@property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle; -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m deleted file mode 100644 index 4d42de6..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m +++ /dev/null @@ -1,118 +0,0 @@ -// -// MJRefreshBackNormalFooter.m -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshBackNormalFooter.h" - -@interface MJRefreshBackNormalFooter() -{ - __unsafe_unretained UIImageView *_arrowView; -} -@property (weak, nonatomic) UIActivityIndicatorView *loadingView; -@end - -@implementation MJRefreshBackNormalFooter -#pragma mark - 懒加载子控件 -- (UIImageView *)arrowView -{ - if (!_arrowView) { - UIImage *image = [UIImage imageNamed:MJRefreshSrcName(@"arrow.png")] ?: [UIImage imageNamed:MJRefreshFrameworkSrcName(@"arrow.png")]; - UIImageView *arrowView = [[UIImageView alloc] initWithImage:image]; - [self addSubview:_arrowView = arrowView]; - } - return _arrowView; -} - - -- (UIActivityIndicatorView *)loadingView -{ - if (!_loadingView) { - UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle]; - loadingView.hidesWhenStopped = YES; - [self addSubview:_loadingView = loadingView]; - } - return _loadingView; -} - -- (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle -{ - _activityIndicatorViewStyle = activityIndicatorViewStyle; - - self.loadingView = nil; - [self setNeedsLayout]; -} -#pragma makr - 重写父类的方法 -- (void)prepare -{ - [super prepare]; - - self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; -} - -- (void)placeSubviews -{ - [super placeSubviews]; - - // 箭头的中心点 - CGFloat arrowCenterX = self.mj_w * 0.5; - if (!self.stateLabel.hidden) { - arrowCenterX -= 100; - } - CGFloat arrowCenterY = self.mj_h * 0.5; - CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY); - - // 箭头 - if (self.arrowView.constraints.count == 0) { - self.arrowView.mj_size = self.arrowView.image.size; - self.arrowView.center = arrowCenter; - } - - // 圈圈 - if (self.loadingView.constraints.count == 0) { - self.loadingView.center = arrowCenter; - } -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - // 根据状态做事情 - if (state == MJRefreshStateIdle) { - if (oldState == MJRefreshStateRefreshing) { - self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); - [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ - self.loadingView.alpha = 0.0; - } completion:^(BOOL finished) { - self.loadingView.alpha = 1.0; - [self.loadingView stopAnimating]; - - self.arrowView.hidden = NO; - }]; - } else { - self.arrowView.hidden = NO; - [self.loadingView stopAnimating]; - [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ - self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); - }]; - } - } else if (state == MJRefreshStatePulling) { - self.arrowView.hidden = NO; - [self.loadingView stopAnimating]; - [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ - self.arrowView.transform = CGAffineTransformIdentity; - }]; - } else if (state == MJRefreshStateRefreshing) { - self.arrowView.hidden = YES; - [self.loadingView startAnimating]; - } else if (state == MJRefreshStateNoMoreData) { - self.arrowView.hidden = YES; - [self.loadingView stopAnimating]; - } -} - -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h deleted file mode 100644 index 2d02b2e..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// MJRefreshBackStateFooter.h -// MJRefreshExample -// -// Created by MJ Lee on 15/6/13. -// Copyright © 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshBackFooter.h" - -@interface MJRefreshBackStateFooter : MJRefreshBackFooter -/** 显示刷新状态的label */ -@property (weak, nonatomic, readonly) UILabel *stateLabel; -/** 设置state状态下的文字 */ -- (void)setTitle:(NSString *)title forState:(MJRefreshState)state; - -/** 获取state状态下的title */ -- (NSString *)titleForState:(MJRefreshState)state; -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m deleted file mode 100644 index ab453d1..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m +++ /dev/null @@ -1,79 +0,0 @@ -// -// MJRefreshBackStateFooter.m -// MJRefreshExample -// -// Created by MJ Lee on 15/6/13. -// Copyright © 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshBackStateFooter.h" - -@interface MJRefreshBackStateFooter() -{ - /** 显示刷新状态的label */ - __unsafe_unretained UILabel *_stateLabel; -} -/** 所有状态对应的文字 */ -@property (strong, nonatomic) NSMutableDictionary *stateTitles; -@end - -@implementation MJRefreshBackStateFooter -#pragma mark - 懒加载 -- (NSMutableDictionary *)stateTitles -{ - if (!_stateTitles) { - self.stateTitles = [NSMutableDictionary dictionary]; - } - return _stateTitles; -} - -- (UILabel *)stateLabel -{ - if (!_stateLabel) { - [self addSubview:_stateLabel = [UILabel label]]; - } - return _stateLabel; -} - -#pragma mark - 公共方法 -- (void)setTitle:(NSString *)title forState:(MJRefreshState)state -{ - if (title == nil) return; - self.stateTitles[@(state)] = title; - self.stateLabel.text = self.stateTitles[@(self.state)]; -} - -- (NSString *)titleForState:(MJRefreshState)state { - return self.stateTitles[@(state)]; -} - -#pragma mark - 重写父类的方法 -- (void)prepare -{ - [super prepare]; - - // 初始化文字 - [self setTitle:MJRefreshBackFooterIdleText forState:MJRefreshStateIdle]; - [self setTitle:MJRefreshBackFooterPullingText forState:MJRefreshStatePulling]; - [self setTitle:MJRefreshBackFooterRefreshingText forState:MJRefreshStateRefreshing]; - [self setTitle:MJRefreshBackFooterNoMoreDataText forState:MJRefreshStateNoMoreData]; -} - -- (void)placeSubviews -{ - [super placeSubviews]; - - if (self.stateLabel.constraints.count) return; - - // 状态标签 - self.stateLabel.frame = self.bounds; -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - // 设置状态文字 - self.stateLabel.text = self.stateTitles[@(state)]; -} -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h deleted file mode 100644 index ce3ed42..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// MJRefreshGifHeader.h -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshStateHeader.h" - -@interface MJRefreshGifHeader : MJRefreshStateHeader -@property (weak, nonatomic, readonly) UIImageView *gifView; - -/** 设置state状态下的动画图片images 动画持续时间duration*/ -- (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; -- (void)setImages:(NSArray *)images forState:(MJRefreshState)state; -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.m b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.m deleted file mode 100644 index 3671d72..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.m +++ /dev/null @@ -1,118 +0,0 @@ -// -// MJRefreshGifHeader.m -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshGifHeader.h" - -@interface MJRefreshGifHeader() -{ - __unsafe_unretained UIImageView *_gifView; -} -/** 所有状态对应的动画图片 */ -@property (strong, nonatomic) NSMutableDictionary *stateImages; -/** 所有状态对应的动画时间 */ -@property (strong, nonatomic) NSMutableDictionary *stateDurations; -@end - -@implementation MJRefreshGifHeader -#pragma mark - 懒加载 -- (UIImageView *)gifView -{ - if (!_gifView) { - UIImageView *gifView = [[UIImageView alloc] init]; - [self addSubview:_gifView = gifView]; - } - return _gifView; -} - -- (NSMutableDictionary *)stateImages -{ - if (!_stateImages) { - self.stateImages = [NSMutableDictionary dictionary]; - } - return _stateImages; -} - -- (NSMutableDictionary *)stateDurations -{ - if (!_stateDurations) { - self.stateDurations = [NSMutableDictionary dictionary]; - } - return _stateDurations; -} - -#pragma mark - 公共方法 -- (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state -{ - if (images == nil) return; - - self.stateImages[@(state)] = images; - self.stateDurations[@(state)] = @(duration); - - /* 根据图片设置控件的高度 */ - UIImage *image = [images firstObject]; - if (image.size.height > self.mj_h) { - self.mj_h = image.size.height; - } -} - -- (void)setImages:(NSArray *)images forState:(MJRefreshState)state -{ - [self setImages:images duration:images.count * 0.1 forState:state]; -} - -#pragma mark - 实现父类的方法 -- (void)setPullingPercent:(CGFloat)pullingPercent -{ - [super setPullingPercent:pullingPercent]; - NSArray *images = self.stateImages[@(MJRefreshStateIdle)]; - if (self.state != MJRefreshStateIdle || images.count == 0) return; - // 停止动画 - [self.gifView stopAnimating]; - // 设置当前需要显示的图片 - NSUInteger index = images.count * pullingPercent; - if (index >= images.count) index = images.count - 1; - self.gifView.image = images[index]; -} - -- (void)placeSubviews -{ - [super placeSubviews]; - - if (self.gifView.constraints.count) return; - - self.gifView.frame = self.bounds; - if (self.stateLabel.hidden && self.lastUpdatedTimeLabel.hidden) { - self.gifView.contentMode = UIViewContentModeCenter; - } else { - self.gifView.contentMode = UIViewContentModeRight; - self.gifView.mj_w = self.mj_w * 0.5 - 90; - } -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - // 根据状态做事情 - if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) { - NSArray *images = self.stateImages[@(state)]; - if (images.count == 0) return; - - [self.gifView stopAnimating]; - if (images.count == 1) { // 单张图片 - self.gifView.image = [images lastObject]; - } else { // 多张图片 - self.gifView.animationImages = images; - self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; - [self.gifView startAnimating]; - } - } else if (state == MJRefreshStateIdle) { - [self.gifView stopAnimating]; - } -} -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h deleted file mode 100644 index 547d05e..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// MJRefreshNormalHeader.h -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshStateHeader.h" - -@interface MJRefreshNormalHeader : MJRefreshStateHeader -@property (weak, nonatomic, readonly) UIImageView *arrowView; -/** 菊花的样式 */ -@property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle; -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.m b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.m deleted file mode 100644 index e94e0c0..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.m +++ /dev/null @@ -1,119 +0,0 @@ -// -// MJRefreshNormalHeader.m -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshNormalHeader.h" - -@interface MJRefreshNormalHeader() -{ - __unsafe_unretained UIImageView *_arrowView; -} -@property (weak, nonatomic) UIActivityIndicatorView *loadingView; -@end - -@implementation MJRefreshNormalHeader -#pragma mark - 懒加载子控件 -- (UIImageView *)arrowView -{ - if (!_arrowView) { - UIImage *image = [UIImage imageNamed:MJRefreshSrcName(@"arrow.png")] ?: [UIImage imageNamed:MJRefreshFrameworkSrcName(@"arrow.png")]; - UIImageView *arrowView = [[UIImageView alloc] initWithImage:image]; - [self addSubview:_arrowView = arrowView]; - } - return _arrowView; -} - -- (UIActivityIndicatorView *)loadingView -{ - if (!_loadingView) { - UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle]; - loadingView.hidesWhenStopped = YES; - [self addSubview:_loadingView = loadingView]; - } - return _loadingView; -} - -#pragma mark - 公共方法 -- (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle -{ - _activityIndicatorViewStyle = activityIndicatorViewStyle; - - self.loadingView = nil; - [self setNeedsLayout]; -} - -#pragma makr - 重写父类的方法 -- (void)prepare -{ - [super prepare]; - - self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; -} - -- (void)placeSubviews -{ - [super placeSubviews]; - - // 箭头的中心点 - CGFloat arrowCenterX = self.mj_w * 0.5; - if (!self.stateLabel.hidden) { - arrowCenterX -= 100; - } - CGFloat arrowCenterY = self.mj_h * 0.5; - CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY); - - // 箭头 - if (self.arrowView.constraints.count == 0) { - self.arrowView.mj_size = self.arrowView.image.size; - self.arrowView.center = arrowCenter; - } - - // 圈圈 - if (self.loadingView.constraints.count == 0) { - self.loadingView.center = arrowCenter; - } -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - // 根据状态做事情 - if (state == MJRefreshStateIdle) { - if (oldState == MJRefreshStateRefreshing) { - self.arrowView.transform = CGAffineTransformIdentity; - - [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ - self.loadingView.alpha = 0.0; - } completion:^(BOOL finished) { - // 如果执行完动画发现不是idle状态,就直接返回,进入其他状态 - if (self.state != MJRefreshStateIdle) return; - - self.loadingView.alpha = 1.0; - [self.loadingView stopAnimating]; - self.arrowView.hidden = NO; - }]; - } else { - [self.loadingView stopAnimating]; - self.arrowView.hidden = NO; - [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ - self.arrowView.transform = CGAffineTransformIdentity; - }]; - } - } else if (state == MJRefreshStatePulling) { - [self.loadingView stopAnimating]; - self.arrowView.hidden = NO; - [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ - self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); - }]; - } else if (state == MJRefreshStateRefreshing) { - self.loadingView.alpha = 1.0; // 防止refreshing -> idle的动画完毕动作没有被执行 - [self.loadingView startAnimating]; - self.arrowView.hidden = YES; - } -} -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h deleted file mode 100644 index 5909532..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// MJRefreshStateHeader.h -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshHeader.h" - -@interface MJRefreshStateHeader : MJRefreshHeader -#pragma mark - 刷新时间相关 -/** 利用这个block来决定显示的更新时间文字 */ -@property (copy, nonatomic) NSString *(^lastUpdatedTimeText)(NSDate *lastUpdatedTime); -/** 显示上一次刷新时间的label */ -@property (weak, nonatomic, readonly) UILabel *lastUpdatedTimeLabel; - -#pragma mark - 状态相关 -/** 显示刷新状态的label */ -@property (weak, nonatomic, readonly) UILabel *stateLabel; -/** 设置state状态下的文字 */ -- (void)setTitle:(NSString *)title forState:(MJRefreshState)state; -@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.m b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.m deleted file mode 100644 index b6cece3..0000000 --- a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.m +++ /dev/null @@ -1,157 +0,0 @@ -// -// MJRefreshStateHeader.m -// MJRefreshExample -// -// Created by MJ Lee on 15/4/24. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "MJRefreshStateHeader.h" - -@interface MJRefreshStateHeader() -{ - /** 显示上一次刷新时间的label */ - __unsafe_unretained UILabel *_lastUpdatedTimeLabel; - /** 显示刷新状态的label */ - __unsafe_unretained UILabel *_stateLabel; -} -/** 所有状态对应的文字 */ -@property (strong, nonatomic) NSMutableDictionary *stateTitles; -@end - -@implementation MJRefreshStateHeader -#pragma mark - 懒加载 -- (NSMutableDictionary *)stateTitles -{ - if (!_stateTitles) { - self.stateTitles = [NSMutableDictionary dictionary]; - } - return _stateTitles; -} - -- (UILabel *)stateLabel -{ - if (!_stateLabel) { - [self addSubview:_stateLabel = [UILabel label]]; - } - return _stateLabel; -} - -- (UILabel *)lastUpdatedTimeLabel -{ - if (!_lastUpdatedTimeLabel) { - [self addSubview:_lastUpdatedTimeLabel = [UILabel label]]; - } - return _lastUpdatedTimeLabel; -} - -#pragma mark - 公共方法 -- (void)setTitle:(NSString *)title forState:(MJRefreshState)state -{ - if (title == nil) return; - self.stateTitles[@(state)] = title; - self.stateLabel.text = self.stateTitles[@(self.state)]; -} - -#pragma mark - 日历获取在9.x之后的系统使用currentCalendar会出异常。在8.0之后使用系统新API。 -- (NSCalendar *)currentCalendar { - if ([NSCalendar respondsToSelector:@selector(calendarWithIdentifier:)]) { - return [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; - } - return [NSCalendar currentCalendar]; -} - -#pragma mark key的处理 -- (void)setLastUpdatedTimeKey:(NSString *)lastUpdatedTimeKey -{ - [super setLastUpdatedTimeKey:lastUpdatedTimeKey]; - - // 如果label隐藏了,就不用再处理 - if (self.lastUpdatedTimeLabel.hidden) return; - - NSDate *lastUpdatedTime = [[NSUserDefaults standardUserDefaults] objectForKey:lastUpdatedTimeKey]; - - // 如果有block - if (self.lastUpdatedTimeText) { - self.lastUpdatedTimeLabel.text = self.lastUpdatedTimeText(lastUpdatedTime); - return; - } - - if (lastUpdatedTime) { - // 1.获得年月日 - NSCalendar *calendar = [self currentCalendar]; - NSUInteger unitFlags = NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay |NSCalendarUnitHour |NSCalendarUnitMinute; - NSDateComponents *cmp1 = [calendar components:unitFlags fromDate:lastUpdatedTime]; - NSDateComponents *cmp2 = [calendar components:unitFlags fromDate:[NSDate date]]; - - // 2.格式化日期 - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; - if ([cmp1 day] == [cmp2 day]) { // 今天 - formatter.dateFormat = @"今天 HH:mm"; - } else if ([cmp1 year] == [cmp2 year]) { // 今年 - formatter.dateFormat = @"MM-dd HH:mm"; - } else { - formatter.dateFormat = @"yyyy-MM-dd HH:mm"; - } - NSString *time = [formatter stringFromDate:lastUpdatedTime]; - - // 3.显示日期 - self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"最后更新:%@", time]; - } else { - self.lastUpdatedTimeLabel.text = @"最后更新:无记录"; - } -} - -#pragma mark - 覆盖父类的方法 -- (void)prepare -{ - [super prepare]; - - // 初始化文字 - [self setTitle:MJRefreshHeaderIdleText forState:MJRefreshStateIdle]; - [self setTitle:MJRefreshHeaderPullingText forState:MJRefreshStatePulling]; - [self setTitle:MJRefreshHeaderRefreshingText forState:MJRefreshStateRefreshing]; -} - -- (void)placeSubviews -{ - [super placeSubviews]; - - if (self.stateLabel.hidden) return; - - BOOL noConstrainsOnStatusLabel = self.stateLabel.constraints.count == 0; - - if (self.lastUpdatedTimeLabel.hidden) { - // 状态 - if (noConstrainsOnStatusLabel) self.stateLabel.frame = self.bounds; - } else { - CGFloat stateLabelH = self.mj_h * 0.5; - // 状态 - if (noConstrainsOnStatusLabel) { - self.stateLabel.mj_x = 0; - self.stateLabel.mj_y = 0; - self.stateLabel.mj_w = self.mj_w; - self.stateLabel.mj_h = stateLabelH; - } - - // 更新时间 - if (self.lastUpdatedTimeLabel.constraints.count == 0) { - self.lastUpdatedTimeLabel.mj_x = 0; - self.lastUpdatedTimeLabel.mj_y = stateLabelH; - self.lastUpdatedTimeLabel.mj_w = self.mj_w; - self.lastUpdatedTimeLabel.mj_h = self.mj_h - self.lastUpdatedTimeLabel.mj_y; - } - } -} - -- (void)setState:(MJRefreshState)state -{ - MJRefreshCheckState - - // 设置状态文字 - self.stateLabel.text = self.stateTitles[@(state)]; - - // 重新设置key(重新显示时间) - self.lastUpdatedTimeKey = self.lastUpdatedTimeKey; -} -@end diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/arrow@2x.png b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/arrow@2x.png deleted file mode 100755 index b1078de..0000000 Binary files a/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/arrow@2x.png and /dev/null differ diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.h b/Pods/MJRefresh/MJRefresh/MJRefresh.h deleted file mode 100644 index 196e6ec..0000000 --- a/Pods/MJRefresh/MJRefresh/MJRefresh.h +++ /dev/null @@ -1,14 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 - -#import "UIScrollView+MJRefresh.h" -#import "UIScrollView+MJExtension.h" -#import "UIView+MJExtension.h" - -#import "MJRefreshNormalHeader.h" -#import "MJRefreshGifHeader.h" - -#import "MJRefreshBackNormalFooter.h" -#import "MJRefreshBackGifFooter.h" -#import "MJRefreshAutoNormalFooter.h" -#import "MJRefreshAutoGifFooter.h" \ No newline at end of file diff --git a/Pods/MJRefresh/MJRefresh/MJRefreshConst.h b/Pods/MJRefresh/MJRefresh/MJRefreshConst.h deleted file mode 100644 index 60c5fc1..0000000 --- a/Pods/MJRefresh/MJRefresh/MJRefreshConst.h +++ /dev/null @@ -1,66 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -#import -#import - -// 弱引用 -#define MJWeakSelf __weak typeof(self) weakSelf = self; - -// 日志输出 -#ifdef DEBUG -#define MJRefreshLog(...) NSLog(__VA_ARGS__) -#else -#define MJRefreshLog(...) -#endif - -// 过期提醒 -#define MJRefreshDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) - -// 运行时objc_msgSend -#define MJRefreshMsgSend(...) ((void (*)(void *, SEL, UIView *))objc_msgSend)(__VA_ARGS__) -#define MJRefreshMsgTarget(target) (__bridge void *)(target) - -// RGB颜色 -#define MJRefreshColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0] - -// 文字颜色 -#define MJRefreshLabelTextColor MJRefreshColor(90, 90, 90) - -// 字体大小 -#define MJRefreshLabelFont [UIFont boldSystemFontOfSize:14] - -// 图片路径 -#define MJRefreshSrcName(file) [@"MJRefresh.bundle" stringByAppendingPathComponent:file] -#define MJRefreshFrameworkSrcName(file) [@"Frameworks/MJRefresh.framework/MJRefresh.bundle" stringByAppendingPathComponent:file] - -// 常量 -UIKIT_EXTERN const CGFloat MJRefreshHeaderHeight; -UIKIT_EXTERN const CGFloat MJRefreshFooterHeight; -UIKIT_EXTERN const CGFloat MJRefreshFastAnimationDuration; -UIKIT_EXTERN const CGFloat MJRefreshSlowAnimationDuration; - -UIKIT_EXTERN NSString *const MJRefreshKeyPathContentOffset; -UIKIT_EXTERN NSString *const MJRefreshKeyPathContentSize; -UIKIT_EXTERN NSString *const MJRefreshKeyPathContentInset; -UIKIT_EXTERN NSString *const MJRefreshKeyPathPanState; - -UIKIT_EXTERN NSString *const MJRefreshHeaderLastUpdatedTimeKey; - -UIKIT_EXTERN NSString *const MJRefreshHeaderIdleText; -UIKIT_EXTERN NSString *const MJRefreshHeaderPullingText; -UIKIT_EXTERN NSString *const MJRefreshHeaderRefreshingText; - -UIKIT_EXTERN NSString *const MJRefreshAutoFooterIdleText; -UIKIT_EXTERN NSString *const MJRefreshAutoFooterRefreshingText; -UIKIT_EXTERN NSString *const MJRefreshAutoFooterNoMoreDataText; - -UIKIT_EXTERN NSString *const MJRefreshBackFooterIdleText; -UIKIT_EXTERN NSString *const MJRefreshBackFooterPullingText; -UIKIT_EXTERN NSString *const MJRefreshBackFooterRefreshingText; -UIKIT_EXTERN NSString *const MJRefreshBackFooterNoMoreDataText; - -// 状态检查 -#define MJRefreshCheckState \ -MJRefreshState oldState = self.state; \ -if (state == oldState) return; \ -[super setState:state]; diff --git a/Pods/MJRefresh/MJRefresh/MJRefreshConst.m b/Pods/MJRefresh/MJRefresh/MJRefreshConst.m deleted file mode 100644 index 91e04f3..0000000 --- a/Pods/MJRefresh/MJRefresh/MJRefreshConst.m +++ /dev/null @@ -1,28 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -#import - -const CGFloat MJRefreshHeaderHeight = 54.0; -const CGFloat MJRefreshFooterHeight = 44.0; -const CGFloat MJRefreshFastAnimationDuration = 0.25; -const CGFloat MJRefreshSlowAnimationDuration = 0.4; - -NSString *const MJRefreshKeyPathContentOffset = @"contentOffset"; -NSString *const MJRefreshKeyPathContentInset = @"contentInset"; -NSString *const MJRefreshKeyPathContentSize = @"contentSize"; -NSString *const MJRefreshKeyPathPanState = @"state"; - -NSString *const MJRefreshHeaderLastUpdatedTimeKey = @"MJRefreshHeaderLastUpdatedTimeKey"; - -NSString *const MJRefreshHeaderIdleText = @"下拉可以刷新"; -NSString *const MJRefreshHeaderPullingText = @"松开立即刷新"; -NSString *const MJRefreshHeaderRefreshingText = @"正在刷新数据中..."; - -NSString *const MJRefreshAutoFooterIdleText = @"点击或上拉加载更多"; -NSString *const MJRefreshAutoFooterRefreshingText = @"正在加载更多的数据..."; -NSString *const MJRefreshAutoFooterNoMoreDataText = @"已经全部加载完毕"; - -NSString *const MJRefreshBackFooterIdleText = @"上拉可以加载更多"; -NSString *const MJRefreshBackFooterPullingText = @"松开立即加载更多"; -NSString *const MJRefreshBackFooterRefreshingText = @"正在加载更多的数据..."; -NSString *const MJRefreshBackFooterNoMoreDataText = @"已经全部加载完毕"; \ No newline at end of file diff --git a/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.h b/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.h deleted file mode 100644 index 734110f..0000000 --- a/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.h +++ /dev/null @@ -1,23 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// UIScrollView+Extension.h -// MJRefreshExample -// -// Created by MJ Lee on 14-5-28. -// Copyright (c) 2014年 小码哥. All rights reserved. -// - -#import - -@interface UIScrollView (MJExtension) -@property (assign, nonatomic) CGFloat mj_insetT; -@property (assign, nonatomic) CGFloat mj_insetB; -@property (assign, nonatomic) CGFloat mj_insetL; -@property (assign, nonatomic) CGFloat mj_insetR; - -@property (assign, nonatomic) CGFloat mj_offsetX; -@property (assign, nonatomic) CGFloat mj_offsetY; - -@property (assign, nonatomic) CGFloat mj_contentW; -@property (assign, nonatomic) CGFloat mj_contentH; -@end diff --git a/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.m b/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.m deleted file mode 100644 index 6a13f5f..0000000 --- a/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.m +++ /dev/null @@ -1,110 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// UIScrollView+Extension.m -// MJRefreshExample -// -// Created by MJ Lee on 14-5-28. -// Copyright (c) 2014年 小码哥. All rights reserved. -// - -#import "UIScrollView+MJExtension.h" -#import - -@implementation UIScrollView (MJExtension) - -- (void)setMj_insetT:(CGFloat)mj_insetT -{ - UIEdgeInsets inset = self.contentInset; - inset.top = mj_insetT; - self.contentInset = inset; -} - -- (CGFloat)mj_insetT -{ - return self.contentInset.top; -} - -- (void)setMj_insetB:(CGFloat)mj_insetB -{ - UIEdgeInsets inset = self.contentInset; - inset.bottom = mj_insetB; - self.contentInset = inset; -} - -- (CGFloat)mj_insetB -{ - return self.contentInset.bottom; -} - -- (void)setMj_insetL:(CGFloat)mj_insetL -{ - UIEdgeInsets inset = self.contentInset; - inset.left = mj_insetL; - self.contentInset = inset; -} - -- (CGFloat)mj_insetL -{ - return self.contentInset.left; -} - -- (void)setMj_insetR:(CGFloat)mj_insetR -{ - UIEdgeInsets inset = self.contentInset; - inset.right = mj_insetR; - self.contentInset = inset; -} - -- (CGFloat)mj_insetR -{ - return self.contentInset.right; -} - -- (void)setMj_offsetX:(CGFloat)mj_offsetX -{ - CGPoint offset = self.contentOffset; - offset.x = mj_offsetX; - self.contentOffset = offset; -} - -- (CGFloat)mj_offsetX -{ - return self.contentOffset.x; -} - -- (void)setMj_offsetY:(CGFloat)mj_offsetY -{ - CGPoint offset = self.contentOffset; - offset.y = mj_offsetY; - self.contentOffset = offset; -} - -- (CGFloat)mj_offsetY -{ - return self.contentOffset.y; -} - -- (void)setMj_contentW:(CGFloat)mj_contentW -{ - CGSize size = self.contentSize; - size.width = mj_contentW; - self.contentSize = size; -} - -- (CGFloat)mj_contentW -{ - return self.contentSize.width; -} - -- (void)setMj_contentH:(CGFloat)mj_contentH -{ - CGSize size = self.contentSize; - size.height = mj_contentH; - self.contentSize = size; -} - -- (CGFloat)mj_contentH -{ - return self.contentSize.height; -} -@end diff --git a/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.h b/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.h deleted file mode 100644 index 17d4715..0000000 --- a/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.h +++ /dev/null @@ -1,26 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// UIScrollView+MJRefresh.h -// MJRefreshExample -// -// Created by MJ Lee on 15/3/4. -// Copyright (c) 2015年 小码哥. All rights reserved. -// 给ScrollView增加下拉刷新、上拉刷新的功能 - -#import -#import "MJRefreshConst.h" - -@class MJRefreshHeader, MJRefreshFooter; - -@interface UIScrollView (MJRefresh) -/** 下拉刷新控件 */ -@property (strong, nonatomic) MJRefreshHeader *mj_header; -@property (strong, nonatomic) MJRefreshHeader *header MJRefreshDeprecated("使用mj_header"); -/** 上拉刷新控件 */ -@property (strong, nonatomic) MJRefreshFooter *mj_footer; -@property (strong, nonatomic) MJRefreshFooter *footer MJRefreshDeprecated("使用mj_footer"); - -#pragma mark - other -- (NSInteger)mj_totalDataCount; -@property (copy, nonatomic) void (^mj_reloadDataBlock)(NSInteger totalDataCount); -@end diff --git a/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.m b/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.m deleted file mode 100644 index 635022f..0000000 --- a/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.m +++ /dev/null @@ -1,163 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// UIScrollView+MJRefresh.m -// MJRefreshExample -// -// Created by MJ Lee on 15/3/4. -// Copyright (c) 2015年 小码哥. All rights reserved. -// - -#import "UIScrollView+MJRefresh.h" -#import "MJRefreshHeader.h" -#import "MJRefreshFooter.h" -#import - -@implementation NSObject (MJRefresh) - -+ (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2 -{ - method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2)); -} - -+ (void)exchangeClassMethod1:(SEL)method1 method2:(SEL)method2 -{ - method_exchangeImplementations(class_getClassMethod(self, method1), class_getClassMethod(self, method2)); -} - -@end - -@implementation UIScrollView (MJRefresh) - -#pragma mark - header -static const char MJRefreshHeaderKey = '\0'; -- (void)setMj_header:(MJRefreshHeader *)mj_header -{ - if (mj_header != self.mj_header) { - // 删除旧的,添加新的 - [self.mj_header removeFromSuperview]; - [self insertSubview:mj_header atIndex:0]; - - // 存储新的 - [self willChangeValueForKey:@"mj_header"]; // KVO - objc_setAssociatedObject(self, &MJRefreshHeaderKey, - mj_header, OBJC_ASSOCIATION_ASSIGN); - [self didChangeValueForKey:@"mj_header"]; // KVO - } -} - -- (MJRefreshHeader *)mj_header -{ - return objc_getAssociatedObject(self, &MJRefreshHeaderKey); -} - -#pragma mark - footer -static const char MJRefreshFooterKey = '\0'; -- (void)setMj_footer:(MJRefreshFooter *)mj_footer -{ - if (mj_footer != self.mj_footer) { - // 删除旧的,添加新的 - [self.mj_footer removeFromSuperview]; - [self addSubview:mj_footer]; - - // 存储新的 - [self willChangeValueForKey:@"mj_footer"]; // KVO - objc_setAssociatedObject(self, &MJRefreshFooterKey, - mj_footer, OBJC_ASSOCIATION_ASSIGN); - [self didChangeValueForKey:@"mj_footer"]; // KVO - } -} - -- (MJRefreshFooter *)mj_footer -{ - return objc_getAssociatedObject(self, &MJRefreshFooterKey); -} - -#pragma mark - 过期 -- (void)setFooter:(MJRefreshFooter *)footer -{ - self.mj_footer = footer; -} - -- (MJRefreshFooter *)footer -{ - return self.mj_footer; -} - -- (void)setHeader:(MJRefreshHeader *)header -{ - self.mj_header = header; -} - -- (MJRefreshHeader *)header -{ - return self.mj_header; -} - -#pragma mark - other -- (NSInteger)mj_totalDataCount -{ - NSInteger totalCount = 0; - if ([self isKindOfClass:[UITableView class]]) { - UITableView *tableView = (UITableView *)self; - - for (NSInteger section = 0; section - -@interface UIView (MJExtension) -@property (assign, nonatomic) CGFloat mj_x; -@property (assign, nonatomic) CGFloat mj_y; -@property (assign, nonatomic) CGFloat mj_w; -@property (assign, nonatomic) CGFloat mj_h; -@property (assign, nonatomic) CGSize mj_size; -@property (assign, nonatomic) CGPoint mj_origin; -@end diff --git a/Pods/MJRefresh/MJRefresh/UIView+MJExtension.m b/Pods/MJRefresh/MJRefresh/UIView+MJExtension.m deleted file mode 100644 index 7e8eda2..0000000 --- a/Pods/MJRefresh/MJRefresh/UIView+MJExtension.m +++ /dev/null @@ -1,84 +0,0 @@ -// 代码地址: https://github.com/CoderMJLee/MJRefresh -// 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 -// UIView+Extension.m -// MJRefreshExample -// -// Created by MJ Lee on 14-5-28. -// Copyright (c) 2014年 小码哥. All rights reserved. -// - -#import "UIView+MJExtension.h" - -@implementation UIView (MJExtension) -- (void)setMj_x:(CGFloat)mj_x -{ - CGRect frame = self.frame; - frame.origin.x = mj_x; - self.frame = frame; -} - -- (CGFloat)mj_x -{ - return self.frame.origin.x; -} - -- (void)setMj_y:(CGFloat)mj_y -{ - CGRect frame = self.frame; - frame.origin.y = mj_y; - self.frame = frame; -} - -- (CGFloat)mj_y -{ - return self.frame.origin.y; -} - -- (void)setMj_w:(CGFloat)mj_w -{ - CGRect frame = self.frame; - frame.size.width = mj_w; - self.frame = frame; -} - -- (CGFloat)mj_w -{ - return self.frame.size.width; -} - -- (void)setMj_h:(CGFloat)mj_h -{ - CGRect frame = self.frame; - frame.size.height = mj_h; - self.frame = frame; -} - -- (CGFloat)mj_h -{ - return self.frame.size.height; -} - -- (void)setMj_size:(CGSize)mj_size -{ - CGRect frame = self.frame; - frame.size = mj_size; - self.frame = frame; -} - -- (CGSize)mj_size -{ - return self.frame.size; -} - -- (void)setMj_origin:(CGPoint)mj_origin -{ - CGRect frame = self.frame; - frame.origin = mj_origin; - self.frame = frame; -} - -- (CGPoint)mj_origin -{ - return self.frame.origin; -} -@end diff --git a/Pods/MJRefresh/README.md b/Pods/MJRefresh/README.md deleted file mode 100644 index 396d100..0000000 --- a/Pods/MJRefresh/README.md +++ /dev/null @@ -1,358 +0,0 @@ -![(logo)](http://images.cnitblog.com/blog2015/497279/201505/051004492043385.png) -## MJRefresh -* An easy way to use pull-to-refresh -* 用法简单的下拉刷新框架:一行代码搞定 - -## Contents -* Getting Started - * [Features【支持哪些控件的刷新】](#支持哪些控件的刷新) - * [Installation【如何使用MJRefresh】](#如何使用MJRefresh) - * [Who's using【已经超过上百个App正在使用MJRefresh】](#已经超过上百个App正在使用MJRefresh) - * [Classes【MJRefresh类结构图】](#MJRefresh类结构图) -* 常见API - * [MJRefreshComponent.h](#MJRefreshComponent.h) - * [MJRefreshHeader.h](#MJRefreshHeader.h) - * [MJRefreshFooter.h](#MJRefreshFooter.h) - * [MJRefreshAutoFooter.h](#MJRefreshAutoFooter.h) -* Examples - * [Reference【参考】](#参考) - * [下拉刷新01-默认](#下拉刷新01-默认) - * [下拉刷新02-动画图片](#下拉刷新02-动画图片) - * [下拉刷新03-隐藏时间](#下拉刷新03-隐藏时间) - * [下拉刷新04-隐藏状态和时间](#下拉刷新04-隐藏状态和时间) - * [下拉刷新05-自定义文字](#下拉刷新05-自定义文字) - * [下拉刷新06-自定义刷新控件](#下拉刷新06-自定义刷新控件) - * [上拉刷新01-默认](#上拉刷新01-默认) - * [上拉刷新02-动画图片](#上拉刷新02-动画图片) - * [上拉刷新03-隐藏刷新状态的文字](#上拉刷新03-隐藏刷新状态的文字) - * [上拉刷新04-全部加载完毕](#上拉刷新04-全部加载完毕) - * [上拉刷新05-自定义文字](#上拉刷新05-自定义文字) - * [上拉刷新06-加载后隐藏](#上拉刷新06-加载后隐藏) - * [上拉刷新07-自动回弹的上拉01](#上拉刷新07-自动回弹的上拉01) - * [上拉刷新08-自动回弹的上拉02](#上拉刷新08-自动回弹的上拉02) - * [上拉刷新09-自定义刷新控件(自动刷新)](#上拉刷新09-自定义刷新控件(自动刷新)) - * [上拉刷新10-自定义刷新控件(自动回弹)](#上拉刷新10-自定义刷新控件(自动回弹)) - * [UICollectionView01-上下拉刷新](#UICollectionView01-上下拉刷新) - * [UIWebView01-下拉刷新](#UIWebView01-下拉刷新) -* [期待](#期待) - -## 支持哪些控件的刷新 -* `UIScrollView`、`UITableView`、`UICollectionView`、`UIWebView` - -## 如何使用MJRefresh -* cocoapods导入:`pod 'MJRefresh'` -* 手动导入: - * 将`MJRefresh`文件夹中的所有文件拽入项目中 - * 导入主头文件:`#import "MJRefresh.h"` - -```objc -Base Custom -MJRefresh.bundle MJRefresh.h -MJRefreshConst.h MJRefreshConst.m -UIScrollView+MJExtension.h UIScrollView+MJExtension.m -UIScrollView+MJRefresh.h UIScrollView+MJRefresh.m -UIView+MJExtension.h UIView+MJExtension.m -``` - -## 已经超过上百个App正在使用MJRefresh - -* 更多App信息可以关注:[M了个J-博客园](http://www.cnblogs.com/mjios/p/4409853.html) - -## MJRefresh类结构图 -![](http://images0.cnblogs.com/blog2015/497279/201506/132232456139177.png) -- 图中`红色文字的类`:可以直接拿来用 - - 下拉刷新控件的种类 - - 默认(Normal):`MJRefreshNormalHeader` - - 动图(Gif):`MJRefreshGifHeader` - - 上拉刷新控件的种类 - - 自动刷新(Auto) - - 默认(Normal):`MJRefreshAutoNormalFooter` - - 动图(Gif):`MJRefreshAutoGifFooter` - - 自动回弹(Back) - - 默认(Normal):`MJRefreshBackNormalFooter` - - 动图(Gif):`MJRefreshBackGifFooter` -- 图中`非红色文字的类`:拿来继承,用于自定义刷新控件 -- 关于如何自定义刷新控件,可以参考下图的类
- - -## MJRefreshComponent.h -```objc -/** 刷新控件的基类 */ -@interface MJRefreshComponent : UIView -# pragma mark - 刷新状态控制 -/** 进入刷新状态 */ -- (void)beginRefreshing; -/** 结束刷新状态 */ -- (void)endRefreshing; -/** 是否正在刷新 */ -- (BOOL)isRefreshing; - -# pragma mark - 其他 -/** 根据拖拽比例自动切换透明度 */ -@property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha; -@end -``` - -## MJRefreshHeader.h -```objc -@interface MJRefreshHeader : MJRefreshComponent -/** 创建header */ -+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; -/** 创建header */ -+ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; - -/** 这个key用来存储上一次下拉刷新成功的时间 */ -@property (copy, nonatomic) NSString *lastUpdatedTimeKey; -/** 上一次下拉刷新成功的时间 */ -@property (strong, nonatomic, readonly) NSDate *lastUpdatedTime; - -/** 忽略多少scrollView的contentInset的top */ -@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop; -@end -``` - -## MJRefreshFooter.h -```objc -@interface MJRefreshFooter : MJRefreshComponent -/** 创建footer */ -+ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; -/** 创建footer */ -+ (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; - -/** 提示没有更多的数据 */ -- (void)endRefreshingWithNoMoreData; -/** 重置没有更多的数据(消除没有更多数据的状态) */ -- (void)resetNoMoreData; - -/** 忽略多少scrollView的contentInset的bottom */ -@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom; - -/** 自动根据有无数据来显示和隐藏(有数据就显示,没有数据隐藏) */ -@property (assign, nonatomic) BOOL automaticallyHidden; -@end -``` - -## MJRefreshAutoFooter.h -```objc -@interface MJRefreshAutoFooter : MJRefreshFooter -/** 是否自动刷新(默认为YES) */ -@property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh; - -/** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */ -@property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent; -@end -``` - -## 参考 -```objc -* 由于这个框架的功能较多,就不写具体文字描述其用法 -* 大家可以直接参考示例中的MJTableViewController、MJCollectionViewController、MJWebViewController,更为直观快速 -``` - - -## 下拉刷新01-默认 -```objc -self.tableView.header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ - // 进入刷新状态后会自动调用这个block -}]; -或 -// 设置回调(一旦进入刷新状态,就调用target的action,也就是调用self的loadNewData方法) -self.tableView.header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; - -// 马上进入刷新状态 -[self.tableView.header beginRefreshing]; -``` -![(下拉刷新01-普通)](http://images0.cnblogs.com/blog2015/497279/201506/141204343486151.gif) - -## 下拉刷新02-动画图片 -```objc -// 设置回调(一旦进入刷新状态,就调用target的action,也就是调用self的loadNewData方法) -MJRefreshGifHeader *header = [MJRefreshGifHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; -// 设置普通状态的动画图片 -[header setImages:idleImages forState:MJRefreshStateIdle]; -// 设置即将刷新状态的动画图片(一松开就会刷新的状态) -[header setImages:pullingImages forState:MJRefreshStatePulling]; -// 设置正在刷新状态的动画图片 -[header setImages:refreshingImages forState:MJRefreshStateRefreshing]; -// 设置header -self.tableView.mj_header = header; -``` -![(下拉刷新02-动画图片)](http://images0.cnblogs.com/blog2015/497279/201506/141204402238389.gif) - -## 下拉刷新03-隐藏时间 -```objc -// 隐藏时间 -header.lastUpdatedTimeLabel.hidden = YES; -``` -![(下拉刷新03-隐藏时间)](http://images0.cnblogs.com/blog2015/497279/201506/141204456132944.gif) - -## 下拉刷新04-隐藏状态和时间 -```objc -// 隐藏时间 -header.lastUpdatedTimeLabel.hidden = YES; - -// 隐藏状态 -header.stateLabel.hidden = YES; -``` -![(下拉刷新04-隐藏状态和时间0)](http://images0.cnblogs.com/blog2015/497279/201506/141204508639539.gif) - -## 下拉刷新05-自定义文字 -```objc -// 设置文字 -[header setTitle:@"Pull down to refresh" forState:MJRefreshStateIdle]; -[header setTitle:@"Release to refresh" forState:MJRefreshStatePulling]; -[header setTitle:@"Loading ..." forState:MJRefreshStateRefreshing]; - -// 设置字体 -header.stateLabel.font = [UIFont systemFontOfSize:15]; -header.lastUpdatedTimeLabel.font = [UIFont systemFontOfSize:14]; - -// 设置颜色 -header.stateLabel.textColor = [UIColor redColor]; -header.lastUpdatedTimeLabel.textColor = [UIColor blueColor]; -``` -![(下拉刷新05-自定义文字)](http://images0.cnblogs.com/blog2015/497279/201506/141204563633593.gif) - -## 下拉刷新06-自定义刷新控件 -```objc -self.tableView.mj_header = [MJDIYHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; -// 具体实现参考MJDIYHeader.h和MJDIYHeader.m -``` -![(下拉刷新06-自定义刷新控件)](http://images0.cnblogs.com/blog2015/497279/201506/141205019261159.gif) - -## 上拉刷新01-默认 -```objc -self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ - // 进入刷新状态后会自动调用这个block -}]; -或 -// 设置回调(一旦进入刷新状态,就调用target的action,也就是调用self的loadMoreData方法) -self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; -``` -![(上拉刷新01-默认)](http://images0.cnblogs.com/blog2015/497279/201506/141205090047696.gif) - -## 上拉刷新02-动画图片 -```objc -// 设置回调(一旦进入刷新状态,就调用target的action,也就是调用self的loadMoreData方法) -MJRefreshAutoGifFooter *footer = [MJRefreshAutoGifFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; - -// 设置刷新图片 -[footer setImages:refreshingImages forState:MJRefreshStateRefreshing]; - -// 设置尾部 -self.tableView.mj_footer = footer; -``` -![(上拉刷新02-动画图片)](http://images0.cnblogs.com/blog2015/497279/201506/141205141445793.gif) - -## 上拉刷新03-隐藏刷新状态的文字 -```objc -// 隐藏刷新状态的文字 -footer.refreshingTitleHidden = YES; -// 如果没有上面的方法,就用footer.stateLabel.hidden = YES; -``` -![(上拉刷新03-隐藏刷新状态的文字)](http://images0.cnblogs.com/blog2015/497279/201506/141205200985774.gif) - -## 上拉刷新04-全部加载完毕 -```objc -// 变为没有更多数据的状态 -[footer endRefreshingWithNoMoreData]; -``` -![(上拉刷新04-全部加载完毕)](http://images0.cnblogs.com/blog2015/497279/201506/141205248634686.gif) - -## 上拉刷新05-自定义文字 -```objc -// 设置文字 -[footer setTitle:@"Click or drag up to refresh" forState:MJRefreshStateIdle]; -[footer setTitle:@"Loading more ..." forState:MJRefreshStateRefreshing]; -[footer setTitle:@"No more data" forState:MJRefreshStateNoMoreData]; - -// 设置字体 -footer.stateLabel.font = [UIFont systemFontOfSize:17]; - -// 设置颜色 -footer.stateLabel.textColor = [UIColor blueColor]; -``` -![(上拉刷新05-自定义文字)](http://images0.cnblogs.com/blog2015/497279/201506/141205295511153.gif) - -## 上拉刷新06-加载后隐藏 -```objc -// 隐藏当前的上拉刷新控件 -self.tableView.mj_footer.hidden = YES; -``` -![(上拉刷新06-加载后隐藏)](http://images0.cnblogs.com/blog2015/497279/201506/141205343481821.gif) - -## 上拉刷新07-自动回弹的上拉01 -```objc -self.tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; -``` -![(上拉刷新07-自动回弹的上拉01)](http://images0.cnblogs.com/blog2015/497279/201506/141205392239231.gif) - -## 上拉刷新08-自动回弹的上拉02 -```objc -MJRefreshBackGifFooter *footer = [MJRefreshBackGifFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; - -// 设置普通状态的动画图片 -[footer setImages:idleImages forState:MJRefreshStateIdle]; -// 设置即将刷新状态的动画图片(一松开就会刷新的状态) -[footer setImages:pullingImages forState:MJRefreshStatePulling]; -// 设置正在刷新状态的动画图片 -[footer setImages:refreshingImages forState:MJRefreshStateRefreshing]; - -// 设置尾部 -self.tableView.mj_footer = footer; -``` -![(上拉刷新07-自动回弹的上拉02)](http://images0.cnblogs.com/blog2015/497279/201506/141205441443628.gif) - -## 上拉刷新09-自定义刷新控件(自动刷新) -```objc -self.tableView.mj_footer = [MJDIYAutoFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; -// 具体实现参考MJDIYAutoFooter.h和MJDIYAutoFooter.m -``` -![(上拉刷新09-自定义刷新控件(自动刷新))](http://images0.cnblogs.com/blog2015/497279/201506/141205500195866.gif) - -## 上拉刷新10-自定义刷新控件(自动回弹) -```objc -self.tableView.mj_footer = [MJDIYBackFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; -// 具体实现参考MJDIYBackFooter.h和MJDIYBackFooter.m -``` -![(上拉刷新10-自定义刷新控件(自动回弹))](http://images0.cnblogs.com/blog2015/497279/201506/141205560666819.gif) - -## UICollectionView01-上下拉刷新 -```objc -// 下拉刷新 -self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ - // 进入刷新状态后会自动调用这个block -}]; - -// 上拉刷新 -self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ - // 进入刷新状态后会自动调用这个block -}]; -``` -![(UICollectionView01-上下拉刷新)](http://images0.cnblogs.com/blog2015/497279/201506/141206021603758.gif) - -## UIWebView01-下拉刷新 -```objc -// 添加下拉刷新控件 -self.webView.scrollView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ - // 进入刷新状态后会自动调用这个block -}]; -``` -![(UICollectionView01-上下拉刷新)](http://images0.cnblogs.com/blog2015/497279/201506/141206080514524.gif) - -## 提醒 -* 本框架纯ARC,兼容的系统>=iOS6.0、iPhone\iPad横竖屏 - -## 期待 -* 如果在使用过程中遇到BUG,希望你能Issues我,谢谢(或者尝试下载最新的框架代码看看BUG修复没有) -* 如果在使用过程中发现功能不够用,希望你能Issues我,我非常想为这个框架增加更多好用的功能,谢谢 -* 如果你想为MJRefresh输出代码,请拼命Pull Requests我 -* 如果你开发的应用中用到了MJRefresh,希望你能到[CocoaControls](https://www.cocoacontrols.com/controls/mjrefresh)添加你应用的iTunes路径,我将会安装使用你的应用,并且根据众多应用的使用情况,对MJRefresh进行一个更好的设计和完善,提供更多好用的功能,谢谢 - * 步骤01(微信是举个例子,百度“你的应用名称 itunes”) -![(step01)](http://ww4.sinaimg.cn/mw1024/800cdf9ctw1eq0viiv5rsj20sm0ea41t.jpg) - * 步骤02 -![(step02)](http://ww2.sinaimg.cn/mw1024/800cdf9ctw1eq0vilejxlj20tu0me7a0.jpg) - * 步骤03 -![(step03)](http://ww1.sinaimg.cn/mw1024/800cdf9ctw1eq0viocpo5j20wc0dc0un.jpg) - * 步骤04 -![(step04)](http://ww3.sinaimg.cn/mw1024/800cdf9ctw1eq0vir137xj20si0gewgu.jpg) diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock deleted file mode 100644 index 736bf1d..0000000 --- a/Pods/Manifest.lock +++ /dev/null @@ -1,71 +0,0 @@ -PODS: - - AFNetworking (3.1.0): - - AFNetworking/NSURLSession (= 3.1.0) - - AFNetworking/Reachability (= 3.1.0) - - AFNetworking/Security (= 3.1.0) - - AFNetworking/Serialization (= 3.1.0) - - AFNetworking/UIKit (= 3.1.0) - - AFNetworking/NSURLSession (3.1.0): - - AFNetworking/Reachability - - AFNetworking/Security - - AFNetworking/Serialization - - AFNetworking/Reachability (3.1.0) - - AFNetworking/Security (3.1.0) - - AFNetworking/Serialization (3.1.0) - - AFNetworking/UIKit (3.1.0): - - AFNetworking/NSURLSession - - DACircularProgress (2.3.1) - - DKNightVersion (2.3.0): - - DKNightVersion/Core (= 2.3.0) - - DKNightVersion/CoreAnimation (= 2.3.0) - - DKNightVersion/UIKit (= 2.3.0) - - DKNightVersion/Core (2.3.0): - - DKNightVersion/Core/DeallocBlockExecutor (= 2.3.0) - - DKNightVersion/Core/extobjc (= 2.3.0) - - DKNightVersion/Core/DeallocBlockExecutor (2.3.0) - - DKNightVersion/Core/extobjc (2.3.0) - - DKNightVersion/CoreAnimation (2.3.0): - - DKNightVersion/Core - - DKNightVersion/UIKit (2.3.0): - - DKNightVersion/Core - - FMDB (2.6.2): - - FMDB/standard (= 2.6.2) - - FMDB/standard (2.6.2) - - MJExtension (3.0.10) - - MJRefresh (3.1.0) - - NJKWebViewProgress (0.2.3): - - NJKWebViewProgress/Core (= 0.2.3) - - NJKWebViewProgress/ProgressView (= 0.2.3) - - NJKWebViewProgress/Core (0.2.3) - - NJKWebViewProgress/ProgressView (0.2.3) - - pop (1.0.9) - - SDWebImage (3.7.5): - - SDWebImage/Core (= 3.7.5) - - SDWebImage/Core (3.7.5) - - SVProgressHUD (2.0.3) - -DEPENDENCIES: - - AFNetworking - - DACircularProgress - - DKNightVersion - - FMDB - - MJExtension - - MJRefresh - - NJKWebViewProgress - - pop - - SDWebImage - - SVProgressHUD - -SPEC CHECKSUMS: - AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 - DACircularProgress: 4dd437c0fc3da5161cb289e07ac449493d41db71 - DKNightVersion: 459438cbfd14edb9e41a777ff14b18e5661b9269 - FMDB: 854a0341b4726e53276f2a8996f06f1b80f9259a - MJExtension: d86aacb740c87519d20e3cca55b6fa4be6cc7548 - MJRefresh: 743e6404967d1c2c688472ea3ecfde247d872db4 - NJKWebViewProgress: f481fd424cb5ecc27eacae11b5397db92fba9a4d - pop: f667631a5108a2e60d9e8797c9b32ddaf2080bce - SDWebImage: 69c6303e3348fba97e03f65d65d4fbc26740f461 - SVProgressHUD: b0830714205bea1317ea1a2ebc71e5633af334d4 - -COCOAPODS: 0.39.0 diff --git a/Pods/NJKWebViewProgress/LICENSE b/Pods/NJKWebViewProgress/LICENSE deleted file mode 100644 index 903fc84..0000000 --- a/Pods/NJKWebViewProgress/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Satoshi Asano - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgress.h b/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgress.h deleted file mode 100644 index 1770c28..0000000 --- a/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgress.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// NJKWebViewProgress.h -// -// Created by Satoshi Aasano on 4/20/13. -// Copyright (c) 2013 Satoshi Asano. All rights reserved. -// - -#import - -#undef njk_weak -#if __has_feature(objc_arc_weak) -#define njk_weak weak -#else -#define njk_weak unsafe_unretained -#endif - -extern const float NJKInitialProgressValue; -extern const float NJKInteractiveProgressValue; -extern const float NJKFinalProgressValue; - -typedef void (^NJKWebViewProgressBlock)(float progress); -@protocol NJKWebViewProgressDelegate; -@interface NJKWebViewProgress : NSObject -@property (nonatomic, njk_weak) idprogressDelegate; -@property (nonatomic, njk_weak) idwebViewProxyDelegate; -@property (nonatomic, copy) NJKWebViewProgressBlock progressBlock; -@property (nonatomic, readonly) float progress; // 0.0..1.0 - -- (void)reset; -@end - -@protocol NJKWebViewProgressDelegate -- (void)webViewProgress:(NJKWebViewProgress *)webViewProgress updateProgress:(float)progress; -@end - diff --git a/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgress.m b/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgress.m deleted file mode 100644 index 9c8bdaa..0000000 --- a/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgress.m +++ /dev/null @@ -1,204 +0,0 @@ -// -// NJKWebViewProgress.m -// -// Created by Satoshi Aasano on 4/20/13. -// Copyright (c) 2013 Satoshi Asano. All rights reserved. -// - -#import "NJKWebViewProgress.h" - -NSString *completeRPCURL = @"webviewprogressproxy:///complete"; - -const float NJKInitialProgressValue = 0.1f; -const float NJKInteractiveProgressValue = 0.5f; -const float NJKFinalProgressValue = 0.9f; - -@implementation NJKWebViewProgress -{ - NSUInteger _loadingCount; - NSUInteger _maxLoadCount; - NSURL *_currentURL; - BOOL _interactive; -} - -- (id)init -{ - self = [super init]; - if (self) { - _maxLoadCount = _loadingCount = 0; - _interactive = NO; - } - return self; -} - -- (void)startProgress -{ - if (_progress < NJKInitialProgressValue) { - [self setProgress:NJKInitialProgressValue]; - } -} - -- (void)incrementProgress -{ - float progress = self.progress; - float maxProgress = _interactive ? NJKFinalProgressValue : NJKInteractiveProgressValue; - float remainPercent = (float)_loadingCount / (float)_maxLoadCount; - float increment = (maxProgress - progress) * remainPercent; - progress += increment; - progress = fmin(progress, maxProgress); - [self setProgress:progress]; -} - -- (void)completeProgress -{ - [self setProgress:1.0]; -} - -- (void)setProgress:(float)progress -{ - // progress should be incremental only - if (progress > _progress || progress == 0) { - _progress = progress; - if ([_progressDelegate respondsToSelector:@selector(webViewProgress:updateProgress:)]) { - [_progressDelegate webViewProgress:self updateProgress:progress]; - } - if (_progressBlock) { - _progressBlock(progress); - } - } -} - -- (void)reset -{ - _maxLoadCount = _loadingCount = 0; - _interactive = NO; - [self setProgress:0.0]; -} - -#pragma mark - -#pragma mark UIWebViewDelegate - -- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType -{ - if ([request.URL.absoluteString isEqualToString:completeRPCURL]) { - [self completeProgress]; - return NO; - } - - BOOL ret = YES; - if ([_webViewProxyDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { - ret = [_webViewProxyDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; - } - - BOOL isFragmentJump = NO; - if (request.URL.fragment) { - NSString *nonFragmentURL = [request.URL.absoluteString stringByReplacingOccurrencesOfString:[@"#" stringByAppendingString:request.URL.fragment] withString:@""]; - isFragmentJump = [nonFragmentURL isEqualToString:webView.request.URL.absoluteString]; - } - - BOOL isTopLevelNavigation = [request.mainDocumentURL isEqual:request.URL]; - - BOOL isHTTP = [request.URL.scheme isEqualToString:@"http"] || [request.URL.scheme isEqualToString:@"https"]; - if (ret && !isFragmentJump && isHTTP && isTopLevelNavigation) { - _currentURL = request.URL; - [self reset]; - } - return ret; -} - -- (void)webViewDidStartLoad:(UIWebView *)webView -{ - if ([_webViewProxyDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) { - [_webViewProxyDelegate webViewDidStartLoad:webView]; - } - - _loadingCount++; - _maxLoadCount = fmax(_maxLoadCount, _loadingCount); - - [self startProgress]; -} - -- (void)webViewDidFinishLoad:(UIWebView *)webView -{ - if ([_webViewProxyDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) { - [_webViewProxyDelegate webViewDidFinishLoad:webView]; - } - - _loadingCount--; - [self incrementProgress]; - - NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"]; - - BOOL interactive = [readyState isEqualToString:@"interactive"]; - if (interactive) { - _interactive = YES; - NSString *waitForCompleteJS = [NSString stringWithFormat:@"window.addEventListener('load',function() { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = '%@'; document.body.appendChild(iframe); }, false);", completeRPCURL]; - [webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS]; - } - - BOOL isNotRedirect = _currentURL && [_currentURL isEqual:webView.request.mainDocumentURL]; - BOOL complete = [readyState isEqualToString:@"complete"]; - if (complete && isNotRedirect) { - [self completeProgress]; - } -} - -- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error -{ - if ([_webViewProxyDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) { - [_webViewProxyDelegate webView:webView didFailLoadWithError:error]; - } - - _loadingCount--; - [self incrementProgress]; - - NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"]; - - BOOL interactive = [readyState isEqualToString:@"interactive"]; - if (interactive) { - _interactive = YES; - NSString *waitForCompleteJS = [NSString stringWithFormat:@"window.addEventListener('load',function() { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = '%@'; document.body.appendChild(iframe); }, false);", completeRPCURL]; - [webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS]; - } - - BOOL isNotRedirect = _currentURL && [_currentURL isEqual:webView.request.mainDocumentURL]; - BOOL complete = [readyState isEqualToString:@"complete"]; - if (complete && isNotRedirect) { - [self completeProgress]; - } -} - -#pragma mark - -#pragma mark Method Forwarding -// for future UIWebViewDelegate impl - -- (BOOL)respondsToSelector:(SEL)aSelector -{ - if ( [super respondsToSelector:aSelector] ) - return YES; - - if ([self.webViewProxyDelegate respondsToSelector:aSelector]) - return YES; - - return NO; -} - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector -{ - NSMethodSignature *signature = [super methodSignatureForSelector:selector]; - if(!signature) { - if([_webViewProxyDelegate respondsToSelector:selector]) { - return [(NSObject *)_webViewProxyDelegate methodSignatureForSelector:selector]; - } - } - return signature; -} - -- (void)forwardInvocation:(NSInvocation*)invocation -{ - if ([_webViewProxyDelegate respondsToSelector:[invocation selector]]) { - [invocation invokeWithTarget:_webViewProxyDelegate]; - } -} - -@end diff --git a/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgressView.h b/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgressView.h deleted file mode 100644 index 0f7a2ad..0000000 --- a/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgressView.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// NJKWebViewProgressView.h -// iOS 7 Style WebView Progress Bar -// -// Created by Satoshi Aasano on 11/16/13. -// Copyright (c) 2013 Satoshi Asano. All rights reserved. -// - -#import - -@interface NJKWebViewProgressView : UIView -@property (nonatomic) float progress; - -@property (nonatomic) UIView *progressBarView; -@property (nonatomic) NSTimeInterval barAnimationDuration; // default 0.1 -@property (nonatomic) NSTimeInterval fadeAnimationDuration; // default 0.27 -@property (nonatomic) NSTimeInterval fadeOutDelay; // default 0.1 - -- (void)setProgress:(float)progress animated:(BOOL)animated; - -@end diff --git a/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgressView.m b/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgressView.m deleted file mode 100644 index bf9fe67..0000000 --- a/Pods/NJKWebViewProgress/NJKWebViewProgress/NJKWebViewProgressView.m +++ /dev/null @@ -1,75 +0,0 @@ -// -// NJKWebViewProgressView.m -// -// Created by Satoshi Aasanoon 11/16/13. -// Copyright (c) 2013 Satoshi Asano. All rights reserved. -// - -#import "NJKWebViewProgressView.h" - -@implementation NJKWebViewProgressView - -- (id)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - if (self) { - [self configureViews]; - } - return self; -} - -- (void)awakeFromNib -{ - [super awakeFromNib]; - [self configureViews]; -} - --(void)configureViews -{ - self.userInteractionEnabled = NO; - self.autoresizingMask = UIViewAutoresizingFlexibleWidth; - _progressBarView = [[UIView alloc] initWithFrame:self.bounds]; - _progressBarView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; - UIColor *tintColor = [UIColor colorWithRed:22.f / 255.f green:126.f / 255.f blue:251.f / 255.f alpha:1.0]; // iOS7 Safari bar color - if ([UIApplication.sharedApplication.delegate.window respondsToSelector:@selector(setTintColor:)] && UIApplication.sharedApplication.delegate.window.tintColor) { - tintColor = UIApplication.sharedApplication.delegate.window.tintColor; - } - _progressBarView.backgroundColor = tintColor; - [self addSubview:_progressBarView]; - - _barAnimationDuration = 0.27f; - _fadeAnimationDuration = 0.27f; - _fadeOutDelay = 0.1f; -} - --(void)setProgress:(float)progress -{ - [self setProgress:progress animated:NO]; -} - -- (void)setProgress:(float)progress animated:(BOOL)animated -{ - BOOL isGrowing = progress > 0.0; - [UIView animateWithDuration:(isGrowing && animated) ? _barAnimationDuration : 0.0 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ - CGRect frame = _progressBarView.frame; - frame.size.width = progress * self.bounds.size.width; - _progressBarView.frame = frame; - } completion:nil]; - - if (progress >= 1.0) { - [UIView animateWithDuration:animated ? _fadeAnimationDuration : 0.0 delay:_fadeOutDelay options:UIViewAnimationOptionCurveEaseInOut animations:^{ - _progressBarView.alpha = 0.0; - } completion:^(BOOL completed){ - CGRect frame = _progressBarView.frame; - frame.size.width = 0; - _progressBarView.frame = frame; - }]; - } - else { - [UIView animateWithDuration:animated ? _fadeAnimationDuration : 0.0 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ - _progressBarView.alpha = 1.0; - } completion:nil]; - } -} - -@end diff --git a/Pods/NJKWebViewProgress/README.md b/Pods/NJKWebViewProgress/README.md deleted file mode 100644 index d8c445b..0000000 --- a/Pods/NJKWebViewProgress/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# NJKWebViewProgress -NJKWebViewProgress is a progress interface library for UIWebView. Currently, UIWebView doesn't have official progress interface. You can implement progress bar for your in-app browser using this module. - -iOS ScreenShot 1 - -NJKWebViewProgress doesn't use CocoaTouch's private methods. It's AppStore safe. - -# Used in Production -- [Yahoo! JAPAN](https://itunes.apple.com/app/yahoo!-japan/id299147843?mt=8) -- [Facebook](https://itunes.apple.com/app/facebook/id284882215?mt=8‎) - -# Requirements -- iOS 4.3 or later -- ARC - -# Usage -Instance `NJKWebViewProgress` and set `UIWebViewDelegate`. If you set `webViewProxyDelegate`, `NJKWebViewProgress` should perform as a proxy object. - -```objc -_progressProxy = [[NJKWebViewProgress alloc] init]; // instance variable -webView.delegate = _progressProxy; -_progressProxy.webViewProxyDelegate = self; -_progressProxy.progressDelegate = self; -``` - -When UIWebView start loading, `NJKWebViewProgress` call delegate method and block with progress. -```objc --(void)webViewProgress:(NJKWebViewProgress *)webViewProgress updateProgress:(float)progress -{ - [progressView setProgress:progress animated:NO]; -} -``` - -```objc -progressProxy.progressBlock = ^(float progress) { - [progressView setProgress:progress animated:NO]; -}; -``` - -You can determine the current state of the document by comparing the `progress` value to one of the provided constants: - -```objc --(void)webViewProgress:(NJKWebViewProgress *)webViewProgress updateProgress:(float)progress -{ - if (progress == NJKInteractiveProgressValue) { - // The web view has finished parsing the document, - // but is still loading sub-resources - } -} -``` - -This repository contains iOS 7 Safari style bar `NJKWebViewProgressView`. You can choose `NJKWebViewProgressView`, `UIProgressView` or your custom bar. - -# Install -## CocoaPods - -``` -pod 'NJKWebViewProgress' -``` - -# License -[Apache]: http://www.apache.org/licenses/LICENSE-2.0 -[MIT]: http://www.opensource.org/licenses/mit-license.php -[GPL]: http://www.gnu.org/licenses/gpl.html -[BSD]: http://opensource.org/licenses/bsd-license.php -[MIT license][MIT]. diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj deleted file mode 100644 index 34c3804..0000000 --- a/Pods/Pods.xcodeproj/project.pbxproj +++ /dev/null @@ -1,10936 +0,0 @@ - - - - - archiveVersion - 1 - classes - - objectVersion - 46 - objects - - 0027C71767B2F5C6007AB990ED6E0F35 - - fileRef - 82E466DC61BD55C67F8005E93FB5A125 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 00B471635688CA88607CBEADA4AE8FB6 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.script.sh - path - Pods-TTNews-frameworks.sh - sourceTree - <group> - - 00BA2602AC2FFE49870F2D1C58836452 - - fileRef - 0A983F965530EC1B18645593310B35BA - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 00F09DB5E05D1208F27161E2F016FE36 - - fileRef - 3B5BBA7F1E4CBCB75601F86E9B2CAC07 - isa - PBXBuildFile - - 01192DA27188D8A96EDAD3BD64D772E0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - DKDeallocBlockExecutor.h - path - DKNightVersion/DeallocBlockExecutor/DKDeallocBlockExecutor.h - sourceTree - <group> - - 01C04156CE60363ED312F653DF579CB7 - - fileRef - 14CA3D62C115EF064698E9318F63C754 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 01FCDC579FF20E0913D058E620D994A4 - - isa - PBXTargetDependency - name - MJExtension - target - 9EAB3E42EF957541E6C3C76D35FD008E - targetProxy - D9F8E672D5CEC1D51345B9535114FC9D - - 02030CCD1586CDA3A48E4F7F535D9E4A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIButton+Night.h - path - DKNightVersion/Manual/UIButton+Night.h - sourceTree - <group> - - 022B549F10007177A43FAB1DD5114F7D - - fileRef - 7651A2978E64E886CE7A622F6F807ACC - isa - PBXBuildFile - - 026CCF569A2125FCB0EE8F152731259E - - fileRef - AA825D3414D0B6F349D814369D29E3CC - isa - PBXBuildFile - - 027DAE81362CCFBA536EF587D6AF890B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIKit+AFNetworking.h - path - UIKit+AFNetworking/UIKit+AFNetworking.h - sourceTree - <group> - - 028E80C92E6914068BF2355A079D3E9B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - FMDB-prefix.pch - sourceTree - <group> - - 03DE154CF25C8F8E64787FD66CAC26F0 - - fileRef - 09CAF9E9C6F04F5C711BFC79FFAEE767 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 04571616A0774568EE308B94E9CA7ADE - - includeInIndex - 1 - isa - PBXFileReference - name - TransformationMatrix.cpp - path - pop/WebCore/TransformationMatrix.cpp - sourceTree - <group> - - 04EE7FD0A4668B0BF15D595695752EF5 - - children - - 4B23A701F63434F5D75BB2E66D85DAA8 - A6D80A2219B8458E2D2DA26EEF530CA2 - - isa - PBXGroup - name - extobjc - sourceTree - <group> - - 050B66E154969A68944942A22BD9B314 - - buildActionMask - 2147483647 - files - - CA027706DD6805766F1F4EC548E72440 - A350C5704A1F233DAD830E081E00F8C1 - 0EF8E9AA652DBE2745A7D5C8CD2E3BDE - 282081E035E7A843068E8BCC2E07C217 - 3D0F69C4CA35EB6A1F686BA88B19B4F6 - DEFA3504695E9DFDE1177DAB22884470 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 056F69DC67B5670E3D2B2DDF873B0850 - - buildActionMask - 2147483647 - files - - B36EEF4BF268A46C305BD7A34F373D0C - A127139E6D344D33780C1F44A44E59CA - 94A5358AF7E02BD7D58E45AC1FE0AD3F - D42AB85B0E0CC63FA67D04E1842BB85D - 10C305BE4A085364BECC8FFE63AA238B - 53D2801BD1EA151DE56FD79DD7977A43 - C4B2749768B6C5B49E954FC0B47D749B - 9894E4DB291E5043EA8FC890F28E24C4 - 740482D4A2AE34227F9413D08B166514 - E5D09EBCCC6B815D71144CF58EDBE648 - 9B2BAE79E21A89F45EB6446993E9761E - 8B5158A4ABC68EBCE0EC603D1C4520D3 - D5CA8819E10405553F9282338F19E0B3 - E000453534D28C45753A7F4EF281BC97 - 4FB4A85823C5A9D8B59B81C80CA3CF78 - C0EB2BD985AFD7BF45BACFD7BA78D0DB - C8924793F687FCA9978BA8B646B9F11E - 8B5E66EEADBA561A5198CB206BD4D66F - 8AC81995D070E176D6151C42705E5FEB - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 0650713BFA545A5D8E01766A79CE43EA - - fileRef - 4AA4EC14BE538143AA9A6074CA3DCEBE - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 065DE18CD0F7516CA7E14DE658589908 - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - B56BC1B5AF7240A3B2E50EC7A163A638 - remoteInfo - SVProgressHUD - - 0694A1D2B7E243582036E55804D87ACF - - fileRef - F8755731C347BA806518AB8BDA3A5644 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 06BFEB1F7510B1E9C1F7A1DE02C3E17D - - baseConfigurationReference - 28A55EC5036EDF3C43CE79A6F884BE7A - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/MJExtension/MJExtension-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - 072C184628072EDE522629576F1716DE - - fileRef - 358C18D56505647A3BFBAE84A7B11F5A - isa - PBXBuildFile - - 07FC64DEA7A7CD353B15D6EFC9499733 - - fileRef - 2BB14DC84BEB4B182F9995C340DE4D65 - isa - PBXBuildFile - - 087DD8E87123B73129EC27879DC1CB09 - - fileRef - 41872B66CA6D947F4A3386450AC8F7EE - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 08DA05B63C2B0C5F9C392BDCA39979FA - - fileRef - 72645AB555ACB154314B9E0E078A93C1 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 091C6AC479BB1B465BF293191BA4EB90 - - isa - PBXFileReference - lastKnownFileType - wrapper.framework - name - ImageIO.framework - path - Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/ImageIO.framework - sourceTree - DEVELOPER_DIR - - 09B52E7DDE3BC06C71347E608236D9EB - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimationTracer.h - path - pop/POPAnimationTracer.h - sourceTree - <group> - - 09C8E3712D8323A3E21C99206F86CCDF - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - NSObject+MJCoding.h - path - MJExtension/NSObject+MJCoding.h - sourceTree - <group> - - 09CAF9E9C6F04F5C711BFC79FFAEE767 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIProgressView+Night.h - path - DKNightVersion/UIKit/UIProgressView+Night.h - sourceTree - <group> - - 09D04A9AB71B5A4FE9175F80496EA5FD - - fileRef - A62DFBB56609C41B67DA82DB9ABB534F - isa - PBXBuildFile - - 0A08FC71C4E3B8E0F8734BCDA7CCA7CB - - fileRef - BCE913E0DA0046134F707A592D2E858C - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 0A3B4632BDEF188FCC6093F85498FABD - - buildConfigurations - - 7918D61FAAD756712F39EB9EEDB1033E - F2B999DB8AF314B4036F5CB2CA1E9657 - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 0A439270E7FB7FE4B0CB65E3E6EC98D3 - - buildActionMask - 2147483647 - files - - FE2464AD4F60C46EDCDB721E788CE1E1 - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 0A6A3B2BDB42B167BC55FE07F4D601BC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJPropertyType.h - path - MJExtension/MJPropertyType.h - sourceTree - <group> - - 0A983F965530EC1B18645593310B35BA - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UINavigationBar+Night.h - path - DKNightVersion/UIKit/UINavigationBar+Night.h - sourceTree - <group> - - 0ACBCC40B07594F01081CE558F1E4979 - - children - - 520DCAEA28F613CEA694518D3199C2E4 - 983909C97559B4E35B0A6454865591B0 - 392C57DDFB9BFE4D900621FCE8EBB201 - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/MJRefresh - sourceTree - <group> - - 0B18F2E823D3FC778625EC66708088FB - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIRefreshControl+AFNetworking.h - path - UIKit+AFNetworking/UIRefreshControl+AFNetworking.h - sourceTree - <group> - - 0B678E4E7D716E2C6EC64BDB96418C7E - - buildActionMask - 2147483647 - files - - D054768D94E678B8A0D7F2AB4FF45E54 - FF1FEB28260D52C222E2F87FBBC05277 - EFCDCCD66269CA908494B11E024DC736 - 9BF8D708AC9892125B0AA4E540361FF8 - C66A994F14BA080776BB3E2AD8CC55B4 - B5A8A60810B99D763E3A3F076F63509D - 6CF9B693754B004AD7FB2F8A6C0C8F36 - 6E80858B842657D04234289E938017AC - F58455418F190BC37D1FD19EA3EA5B01 - F432A374A040EBAB69C9EABC6C4D699A - F7B852F0917A6D6540DAB54F736D681B - 44F50504254959A81B2E96AD6261B2A9 - D595C17C62C58E80EEC6F9EBCCF4050A - EA03AF12E0E3E8ADC50DA70F2C5BF39E - C67F32FF951D1979EF0E6B4ADED5E8A8 - 444602F3A422E5EC1DA8EF2A3DFA01EB - B9B44F1EDC36C095666CA0AE98371C09 - 904EC3379A9E226064CB03DC1ACA2EEA - 77F797A5A72065EE7351CF41728C2EC3 - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 0B6CAFF0BAA701C00CBDE7665A050999 - - fileRef - 94DF3A6264A6FDA5D4C1C563D39B3267 - isa - PBXBuildFile - - 0B7448F84E1B4C725EE8C8A30AE4C1BF - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - AFNetworkReachabilityManager.m - path - AFNetworking/AFNetworkReachabilityManager.m - sourceTree - <group> - - 0C068AB4DD5B93B3AEAF97DCF2B4E61F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - DALabeledCircularProgressView.h - path - DACircularProgress/DALabeledCircularProgressView.h - sourceTree - <group> - - 0C1A29BFF65C6F2801B1BE0058718A19 - - fileRef - 4D9F1AA82224FB855E013A5F56B17A1C - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 0C54C5B996478DAE7DBCED96888191A5 - - fileRef - 0D71858E89B235431D11E96EADBE82A0 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 0C62BAD2FB539FC25C032D174B2FDA84 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - DKNightVersion-dummy.m - sourceTree - <group> - - 0CB3C59B22A9FC7BC0629E03825BE0C3 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshStateHeader.h - path - MJRefresh/Custom/Header/MJRefreshStateHeader.h - sourceTree - <group> - - 0D71858E89B235431D11E96EADBE82A0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SDWebImageCompat.m - path - SDWebImage/SDWebImageCompat.m - sourceTree - <group> - - 0EB5E89BB17510AE9D4971A42147A390 - - baseConfigurationReference - 9FEE2BAE7A1351C991273FC66949E3F1 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/AFNetworking/AFNetworking-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - 0EF8E9AA652DBE2745A7D5C8CD2E3BDE - - fileRef - CFEC0EB589B90099B25BF510F4AB93C0 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 0F43FEACB0C18DD4C724B1655AF47728 - - fileRef - 0C068AB4DD5B93B3AEAF97DCF2B4E61F - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 0F7EC02E944F8719A5EBDC9D29F9E99F - - fileRef - 4DB59CEFDCAB10EE888132C34E5A49AF - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 0F8A8E0D6FAEBF758E0B4D620335F094 - - fileRef - 5B2724ADEAAEE9110491118E803665E2 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 1092242FE516D192B1B18C66F9622DFA - - fileRef - 870CE7053EE55247E23C49C37448C82A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 10B7C108490F0D7DF9D60FA8088927D0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - AFSecurityPolicy.m - path - AFNetworking/AFSecurityPolicy.m - sourceTree - <group> - - 10C305BE4A085364BECC8FFE63AA238B - - fileRef - 58FEA18A9A972E123E965073E1D32DE2 - isa - PBXBuildFile - - 1163222946770435278A243AA92FCC4C - - fileRef - 71C731B37D2EA9A02A3DA8088CA67AE9 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 118227C43F283C87130BEE2BFABC7EBD - - fileRef - 09C8E3712D8323A3E21C99206F86CCDF - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 11DDA56DBF5BB9C523903E0BC8C95B93 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - AFNetworkReachabilityManager.h - path - AFNetworking/AFNetworkReachabilityManager.h - sourceTree - <group> - - 1246A1C00980422020A6884856143651 - - buildActionMask - 2147483647 - files - - 0C1A29BFF65C6F2801B1BE0058718A19 - 0F43FEACB0C18DD4C724B1655AF47728 - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 12648EC322A77F3656B66E366C0E4817 - - fileRef - AC5590B6E86B02889D07FB7A3716BD30 - isa - PBXBuildFile - - 12EB8DEBD812483C68180606AC1B04D2 - - fileRef - 21DBABD607D01F8BF09A40D3B059CEF1 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 132B1F6F7709C80586D8D64312135193 - - fileRef - D2259CB60A2F9266A9285E57A74AC735 - isa - PBXBuildFile - - 133B30495D945B737235B107AAE7DAAB - - fileRef - 3989E7406CED91A29F1CB2841D65B63B - isa - PBXBuildFile - - 13695CE9BF36F26CA806C5CD2D6ADECA - - fileRef - 15DD053288012DFB4833EF207C32D62A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 14271704DDA019ADD853EB2483AC858A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - AFAutoPurgingImageCache.h - path - UIKit+AFNetworking/AFAutoPurgingImageCache.h - sourceTree - <group> - - 14BA402BA3FC8DEFA68ED65ED0F5AD36 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UINavigationBar+Night.m - path - DKNightVersion/UIKit/UINavigationBar+Night.m - sourceTree - <group> - - 14CA3D62C115EF064698E9318F63C754 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimationTracerInternal.h - path - pop/POPAnimationTracerInternal.h - sourceTree - <group> - - 14CD3DFB4A25BAEFB205730FA6750025 - - fileRef - 2483254F23855294248B0307F56706FA - isa - PBXBuildFile - - 14E98BAF93AF8625043D2CF46DCF93CD - - fileRef - 3319B4ED89F1B99A3011A4015C9C243D - isa - PBXBuildFile - - 15460ABD372C937E2A07A2FCDF98C473 - - fileRef - EBD492D43A3A67F231799ADEBD06BBCD - isa - PBXBuildFile - - 1574985E68B39F87E1D366F6CA973F86 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimationRuntime.h - path - pop/POPAnimationRuntime.h - sourceTree - <group> - - 15DD053288012DFB4833EF207C32D62A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJFoundation.h - path - MJExtension/MJFoundation.h - sourceTree - <group> - - 15E877BFB519D3D33A393595838FCB6B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPVector.h - path - pop/POPVector.h - sourceTree - <group> - - 162B149B0DD54501D7C75154E5B604EE - - baseConfigurationReference - 9FEE2BAE7A1351C991273FC66949E3F1 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/AFNetworking/AFNetworking-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - 16BE2CD67995E70CB610D3E6C3F59356 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - AFNetworkActivityIndicatorManager.m - path - UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m - sourceTree - <group> - - 1744716C52C43FDFC4BAFE44AD7C8002 - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - 53406FD23904169597858D7716FC7645 - remoteInfo - pop - - 17475EC41BFAAD98F4661BC5AE841816 - - fileRef - FEF1AFDFDBAEC5C7E99878879EF7A823 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 17896FF148C391D06F24C4109867E0B9 - - fileRef - FBB483FDC0150E805753B9E16F368216 - isa - PBXBuildFile - - 189721A18CF2CD473CD4F76531C3BF3C - - baseConfigurationReference - 240C0A0FA89CBC560EE6CAE6C09D3F78 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/DKNightVersion/DKNightVersion-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - 18C516E3C7EF5B8FE88EFF08CBD156DE - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshBackFooter.m - path - MJRefresh/Base/MJRefreshBackFooter.m - sourceTree - <group> - - 1962B9F0C1051AECDA8B89D66CD291FC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UITextField+Night.h - path - DKNightVersion/UIKit/UITextField+Night.h - sourceTree - <group> - - 198E7D67D49F0C515CB8F4572BF27327 - - fileRef - 390107B74A8C6B534B6A4EB9B296130F - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 19E6319F6ECED7539AABBD9A211F7AEC - - fileRef - 14271704DDA019ADD853EB2483AC858A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 1A5C4AB96ED7AE020BE2FBABF668A88E - - isa - PBXFileReference - lastKnownFileType - wrapper.framework - name - SystemConfiguration.framework - path - Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/SystemConfiguration.framework - sourceTree - DEVELOPER_DIR - - 1A5D9F2992A23D91E8096928C3C7AB7C - - buildActionMask - 2147483647 - files - - 12648EC322A77F3656B66E366C0E4817 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 1B2469D505D02B61ED7C6C6F7879A5E9 - - isa - PBXTargetDependency - name - pop - target - 53406FD23904169597858D7716FC7645 - targetProxy - 1744716C52C43FDFC4BAFE44AD7C8002 - - 1B514CAC6F419732F611364C49453788 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - NJKWebViewProgress-prefix.pch - sourceTree - <group> - - 1BA3D9FB5D5145BF698C941C0F843518 - - fileRef - 543D77F621A899DDD24AFC3E7F1DE129 - isa - PBXBuildFile - - 1BFEBB99F8B6C29BC3E0ED76F43BF9A6 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshNormalHeader.h - path - MJRefresh/Custom/Header/MJRefreshNormalHeader.h - sourceTree - <group> - - 1C8F38CF850A15721D65BF688AB350C2 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - FMDatabase.m - path - src/fmdb/FMDatabase.m - sourceTree - <group> - - 1CB9CD1DACD8CF621FF34E1C9288AB59 - - children - - E2C595890A1C1D2634DFC6C56EEC8D19 - BD4C5BE0A37EB2CD33DF2BE21B98B9B9 - 3C94E0D5ED31DC71CA979DAE02211998 - 30F4ACB997BBCFED40F26559F0C43C0E - 59319D4046C5B66354582598A2E16177 - 0D71858E89B235431D11E96EADBE82A0 - E1D3E01D8670003AC52C24C77D2FA2CF - 7D0852C548A592E2898D99287F864873 - CEC9003853B9FD98D0B888BA4B3D53E5 - C8FE1703C2FE4E4FCE270AB64D41C43B - 3EBB8C52D5B84F0145C7B4843E6D8196 - C24BCF3D9DFDCEDB6A0DD4F3C7592508 - B69CC157C2F527E095D10CB5AA292E49 - 8DC2468DD9EC492AC88E48D891338E9D - 9B324DB188A8DAF715392A21B7786CE7 - ED36DFF964BE5B65765A3A76180A167D - E16024AC613F715D2C527B5D62649D6D - 43BA28469D72B79E4E79BFC6AE7328E9 - F8755731C347BA806518AB8BDA3A5644 - 1E42F9417982CCC0CE97E0BA4FE3BF8A - DBBE23FF83AA9D763461377F8C974E4A - 58B011D090D06A83FAEC96E13AE87181 - B9155B26E8C2310F99E505C74CE287A0 - 9376D50310F2A816C6810D56C7431568 - 80EE313B7591686CDA199AADE3103760 - C39A747A22EF6ADB1D6E1991564E209C - 542FB07FE80287A647887B8ADAB8A460 - 2CD360EF74E638D4D88DCA0C002951CE - F765039FCC5D5506BEC765EE2B818F69 - - isa - PBXGroup - name - Core - sourceTree - <group> - - 1D0CF8793970F85343EE5875F7A8048B - - children - - 1CB9CD1DACD8CF621FF34E1C9288AB59 - 3CB0EC679B1D2007E558221B728BD4E7 - - isa - PBXGroup - name - SDWebImage - path - SDWebImage - sourceTree - <group> - - 1D2A4D35E62D015750C7EBA7FED4C2F9 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - AFURLRequestSerialization.h - path - AFNetworking/AFURLRequestSerialization.h - sourceTree - <group> - - 1E42F9417982CCC0CE97E0BA4FE3BF8A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIImage+GIF.h - path - SDWebImage/UIImage+GIF.h - sourceTree - <group> - - 1F3C80014CF625A855DA90256013B7BF - - fileRef - D6FFC7B986E04B23B6B9E98723DE63D8 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 1F54C462201DF87BDD390BF44DDBC9FB - - fileRef - BD62C61C5E4BCE2BDC8B9AACCDF39161 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 1F6CBEC31D8A4E25692573B7CF63E7A1 - - fileRef - 3EFC21A0B4DE4EF730F826C7C7A1DC75 - isa - PBXBuildFile - - 200CA5D919CC71EA6A769541C92716D3 - - fileRef - F789495B49A442A585248E5D25F41F73 - isa - PBXBuildFile - - 2036F6B0B797484514C2799195C825F4 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIControl+Night.h - path - DKNightVersion/UIKit/UIControl+Night.h - sourceTree - <group> - - 2053D71CF543C3A3CC47C5409D61923C - - fileRef - 23253C4CF8128FBE390F87443BD6562C - isa - PBXBuildFile - - 21889668396C59E8282A365BF440A23A - - fileRef - EFF6557ADDB96E851B58DB7774E0A797 - isa - PBXBuildFile - - 21B5CEBD9CFE81A0C1CEC5A61477671C - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - 21DBABD607D01F8BF09A40D3B059CEF1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UnitBezier.h - path - pop/WebCore/UnitBezier.h - sourceTree - <group> - - 2280D55B18C853A6AA3AE9565EE91F19 - - fileRef - 4497F356CDA07B41213339D445FEDEFD - isa - PBXBuildFile - - 23253C4CF8128FBE390F87443BD6562C - - isa - PBXFileReference - lastKnownFileType - wrapper.framework - name - CoreGraphics.framework - path - Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/CoreGraphics.framework - sourceTree - DEVELOPER_DIR - - 23415204AA48A02959E4E5245CF96037 - - fileRef - B052DBB73F6AC43F438CA23B4F1E454C - isa - PBXBuildFile - - 2359387620C5E815BF0594BC40311734 - - fileRef - 1D2A4D35E62D015750C7EBA7FED4C2F9 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 23CB853B211A106A6145C73FB410F399 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshAutoGifFooter.m - path - MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m - sourceTree - <group> - - 240C0A0FA89CBC560EE6CAE6C09D3F78 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - DKNightVersion.xcconfig - sourceTree - <group> - - 241AFDF1A45B8F607AB3201F5B4B8D9D - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - DKImage.h - path - DKNightVersion/Core/DKImage.h - sourceTree - <group> - - 2483254F23855294248B0307F56706FA - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - AFAutoPurgingImageCache.m - path - UIKit+AFNetworking/AFAutoPurgingImageCache.m - sourceTree - <group> - - 24F963DE832970C9AC8056FD46B23945 - - children - - CD1ADC471EE4AA1560ED050C3D73EF73 - 5BA98B517FE220F0B5FFC21BF1D6D0F1 - 870CE7053EE55247E23C49C37448C82A - 445E1D26937A0AB389327E69E4E1D449 - 52779F8EC04ABF3285125604A009221B - F45FCD3DF9B8E3EBBBF37B99BB1AC646 - 40A3665D2BDD3A5083215B93B2B635A6 - B99088FC0CED9CF0C12C78E9A7FD5965 - 8E6FEA50B8951C38D0598B79D0DDD30D - C74208C0F7AE5568BAE95BB70E4889BC - 3FB139F6D9B19151F259D5C8F3914AE5 - 58FEA18A9A972E123E965073E1D32DE2 - CC180EE0FCC1779DBDAEB2D74C4233A3 - 2E6CF32A69836EB96102B6C209882BE2 - 1574985E68B39F87E1D366F6CA973F86 - F800B3FA3D41FC047B44DA896BDAC4FF - 09B52E7DDE3BC06C71347E608236D9EB - 7DB89CAFE4A77D0845E3A529F4C33236 - 14CA3D62C115EF064698E9318F63C754 - 55D78C4B87AD2EB1128B1EF573DCA07E - 333665A0FC00013B7E8D903B093EB4DB - 93313A2703EA8405C961C7A4A93EBA00 - 8C3614DFB86BEE3C5D8AF4C440835FEC - 5AFC57DB29B71A94CDD967D606771F8C - 356C74FFF66E2F05F1D8B09ECB1CA2C2 - 6C13713E9ACF79B1C9DD83BB5294A0F4 - 4F383980D527FBF4E1E59446EBDCABE4 - 3CA795C0836DC5D571CC4D6853CEEAFC - 5CD308414EC21218DF0AFB748B815ACD - 9C739E488F9426EB75D8770B832A689E - F2A1B494417E8906E69CBCCC9377708F - 71C731B37D2EA9A02A3DA8088CA67AE9 - D07FFE9E17E9D36E5975493AE2B42A47 - BE84F592718A7860CA775F488A519E13 - A12D5C435F39CD32EC30EA1963C64652 - FEF1AFDFDBAEC5C7E99878879EF7A823 - A547A69F558C5AF4063116B5107E2291 - 6146D3EDA291EDE0D05EADE5E305664A - 7188F0957099741427D36245A6E54FB6 - B736674E245CEF6684053B0233339ED7 - CD02648297C3D220A821B3894D170720 - 8CFB044742F1F636842E0564C79D3B66 - 4AA4EC14BE538143AA9A6074CA3DCEBE - 5C217FF8A1665FCC43504CF1EF23C2F5 - 5A4FF6B2DD2604820B26CF0E00ADFC20 - 420E44B31095ED6C5B4CCF417C37EC23 - 15E877BFB519D3D33A393595838FCB6B - FB8E6FDC31229DAA4D8F3B319D62E6D1 - 04571616A0774568EE308B94E9CA7ADE - B9F6903B4A43498CC8B11B1D2B6253A3 - 21DBABD607D01F8BF09A40D3B059CEF1 - AD09B0A836E6685EBF50393BAB033DF7 - - isa - PBXGroup - name - pop - path - pop - sourceTree - <group> - - 250DAA9B5622865808CB870C3D77B732 - - fileRef - B9F6903B4A43498CC8B11B1D2B6253A3 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 25FDEF619A3B63C88174EE0599EFF736 - - buildActionMask - 2147483647 - files - - 599E2A0049425BF2D061F743BF057C31 - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 26CB0A04F3E129D3A030F6CD6312E70D - - fileRef - 6146D3EDA291EDE0D05EADE5E305664A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 2710B519E79C54021DC7079532E108E4 - - fileRef - 8C3614DFB86BEE3C5D8AF4C440835FEC - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 272D2CAB1752F0D1722D5C966DF7DF4F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - FMDatabase.h - path - src/fmdb/FMDatabase.h - sourceTree - <group> - - 277123334AA32A5F4F47DB30CADF4A6A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIScrollView+MJExtension.h - path - MJRefresh/UIScrollView+MJExtension.h - sourceTree - <group> - - 27987FFC8A7521500DDDFF1913E34AF6 - - fileRef - 8FB3C14E9AA647EFCF537833D7CDA965 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 27EAC25242A69C58BBF8A63D030D30CB - - baseConfigurationReference - 520DCAEA28F613CEA694518D3199C2E4 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/MJRefresh/MJRefresh-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - 282081E035E7A843068E8BCC2E07C217 - - fileRef - 7D204CC6422F92FB62FF0384DAEE8A83 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 2894369EF8D702DF360B6763B36E8444 - - fileRef - F4053A20576AD8944AD6FD2D936A722F - isa - PBXBuildFile - - 28A55EC5036EDF3C43CE79A6F884BE7A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - MJExtension.xcconfig - sourceTree - <group> - - 28CBA4F785D01A442C72A29EB6997242 - - fileRef - DBBE23FF83AA9D763461377F8C974E4A - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 29DCD5202C4BAA080E0AEA92548F16CE - - fileRef - BF780D48A3F3F6BB7106595FF27324B1 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 2A16DA566AF62EF3721B19BA70B66319 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libMJRefresh.a - path - libMJRefresh.a - sourceTree - BUILT_PRODUCTS_DIR - - 2A60A16603958DEEDDE0C61B685BA98F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefresh.h - path - MJRefresh/MJRefresh.h - sourceTree - <group> - - 2B10B5DC0A1B4179F8200DEA4CF98984 - - fileRef - B69CC157C2F527E095D10CB5AA292E49 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 2B897B9F53B037B383EBFC742C5150D2 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - AFNetworking-prefix.pch - sourceTree - <group> - - 2BB14DC84BEB4B182F9995C340DE4D65 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UITextField+Keyboard.m - path - DKNightVersion/Manual/UITextField+Keyboard.m - sourceTree - <group> - - 2C57CC0E4887851E2CBD72D0C00A7BAD - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - DACircularProgress.xcconfig - sourceTree - <group> - - 2C59234AF0BDEBC10590AA6CEF12A1DF - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - 2CBFB844B4D7FEB4FF2F11CAE31CAD75 - - buildActionMask - 2147483647 - files - - 2CC27528C64733F5C0AB4B1B6EE35E8C - FDF30F97BDF167F0B956DED0F2C8DA69 - 710DA5E9C44A1BC56CD13FC1F2D6E099 - A0AD231A11A54F59BC8D486946D2928B - A60A4B3054017180C0B6C7B24E651E0A - 4C61B999DF40B1B00449A7AFCBB61D5E - 026CCF569A2125FCB0EE8F152731259E - CDA4DB994BAC351F4CD71EC283055E57 - B9C36A50069E553B4B8538E291864BB5 - CD4CF5F1389281BCE40DF30AD1945D03 - D1D1F11808DACCA88C55636F85A58FD8 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 2CC27528C64733F5C0AB4B1B6EE35E8C - - fileRef - 37BA0D6FAA1EF073108C8D0F05CC1EFA - isa - PBXBuildFile - - 2CD360EF74E638D4D88DCA0C002951CE - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIView+WebCacheOperation.h - path - SDWebImage/UIView+WebCacheOperation.h - sourceTree - <group> - - 2D71E24DE096392A15B6E0B0CE98052E - - fileRef - 15E877BFB519D3D33A393595838FCB6B - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 2D8E8EC45A3A1A1D94AE762CB5028504 - - buildConfigurations - - A70CDAD61F90AC503C7D04CC22DA2923 - FB45FFD90572718D82AB9092B750F0CA - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 2DB64823647E73A39B63E40A7480CF08 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - pop-dummy.m - sourceTree - <group> - - 2E6CF32A69836EB96102B6C209882BE2 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimationPrivate.h - path - pop/POPAnimationPrivate.h - sourceTree - <group> - - 2EB1364F6A10CE22A5B3DE8BB3DB5F74 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIImage+AFNetworking.h - path - UIKit+AFNetworking/UIImage+AFNetworking.h - sourceTree - <group> - - 2F639BAC2BE0057D2AF305181E4637DE - - buildConfigurationList - EB0BD4006D1A94A794ED7E72ABCF46BF - buildPhases - - 1A5D9F2992A23D91E8096928C3C7AB7C - C28B9C6A32E20244B57A39EAF5D1DAF4 - - buildRules - - dependencies - - E9C749C55D347270A4C1F287B1C935AF - 663713375D1E7919975E5E96F6A82802 - DD1D1D97B75E32B3792D585442B300C5 - 75A8688F5F25EF68368B87A723900CEF - 01FCDC579FF20E0913D058E620D994A4 - 780CB4C63BE3C7160E89458A92D284D7 - 7DE8F673DA8EB314243E55B2D0905B53 - 5524A68B285375333C8A6D653BE928E0 - DB89E2C826F5335897375AF92A68A0A7 - 1B2469D505D02B61ED7C6C6F7879A5E9 - - isa - PBXNativeTarget - name - Pods-TTNews - productName - Pods-TTNews - productReference - BF30D702D51DD1ED6EA45DFDB6E99923 - productType - com.apple.product-type.library.static - - 2F74B001E6DE91044F623C89F21EEB18 - - fileRef - DCEDDEE3547FB20129BC913202642782 - isa - PBXBuildFile - - 2F800FBC10F3C6A45CB2C82759F75FD8 - - fileRef - 09B52E7DDE3BC06C71347E608236D9EB - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 302F004632472D55C5BDEC51E19EFE66 - - fileRef - 5A4FF6B2DD2604820B26CF0E00ADFC20 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 30F4ACB997BBCFED40F26559F0C43C0E - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SDImageCache.m - path - SDWebImage/SDImageCache.m - sourceTree - <group> - - 311CDAAE66F1AB4FF57802B6E4B45468 - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - 311E5200832E4AE174A193851A2C6317 - - fileRef - 10B7C108490F0D7DF9D60FA8088927D0 - isa - PBXBuildFile - - 313531CEDA0294311D87B4F4C3F073F4 - - fileRef - 94FF112EB3BB37E6BE359796B4D68C1F - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 324E6E6D50AF00797862F43C84370C95 - - fileRef - C24BCF3D9DFDCEDB6A0DD4F3C7592508 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 32508CD71389B6557800253ED79045E6 - - baseConfigurationReference - 3751D4A2116620F86DA90FCB7C8D82FF - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/FMDB/FMDB-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - 326A543AD4E39A2DAECA1CD385E3AEA2 - - fileRef - A2982E2A64283154B31B5E7106754649 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 32EFF502FBA4473781B2B65FD697B345 - - fileRef - 35254A7C812809C0E8FEB552C28D9300 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 3319B4ED89F1B99A3011A4015C9C243D - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UITextView+Night.m - path - DKNightVersion/UIKit/UITextView+Night.m - sourceTree - <group> - - 333665A0FC00013B7E8D903B093EB4DB - - includeInIndex - 1 - isa - PBXFileReference - name - POPAnimator.mm - path - pop/POPAnimator.mm - sourceTree - <group> - - 346003011339A97DDE1A394DF0A3BB91 - - buildActionMask - 2147483647 - files - - 8F68B37533C5AC9C8588BD0449D32673 - 2894369EF8D702DF360B6763B36E8444 - DA8BAA5F8E5949555BC8B94E309E23D8 - E0529B7C623EB5752662665A05DAAFBA - 5FBA673E534DD7485FD75764258C8046 - A89B33DBDA4E99005E1F1BDB95A296CC - 1BA3D9FB5D5145BF698C941C0F843518 - F0D01ED940723EED434E4EF4BCFF53F9 - 200CA5D919CC71EA6A769541C92716D3 - 9230119085130B273ECF20F076081DA1 - 922ACEFE37E22A36A6F0209742B7B51A - 6EE360D085C7EDD97B0A48A415012D37 - 585CDAC34F08FB220FF26FF55CA899B7 - 133B30495D945B737235B107AAE7DAAB - B19CA8B0FD349AEAD2D5A1C6B46AF47D - 5CBFDC026AAED77902F3CAC48A96FE69 - 932430D4A2289A39BE7C50EEAA60246A - 09D04A9AB71B5A4FE9175F80496EA5FD - 2280D55B18C853A6AA3AE9565EE91F19 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 34D133AE0C68848DDAEAD044A0B123BE - - children - - 240C0A0FA89CBC560EE6CAE6C09D3F78 - 0C62BAD2FB539FC25C032D174B2FDA84 - 7D865DF7D635C708FFE394C76B4650F4 - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/DKNightVersion - sourceTree - <group> - - 3511AE7ADBB71D05565854F0E70B7D08 - - fileRef - A0012A686D96FC5333B2F6A5BF1E9934 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 35254A7C812809C0E8FEB552C28D9300 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - NJKWebViewProgress.h - path - NJKWebViewProgress/NJKWebViewProgress.h - sourceTree - <group> - - 356C74FFF66E2F05F1D8B09ECB1CA2C2 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPBasicAnimationInternal.h - path - pop/POPBasicAnimationInternal.h - sourceTree - <group> - - 358C18D56505647A3BFBAE84A7B11F5A - - isa - PBXFileReference - lastKnownFileType - wrapper.framework - name - QuartzCore.framework - path - Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/QuartzCore.framework - sourceTree - DEVELOPER_DIR - - 35AED4E04DFD8E1B002A1CE8D48851FC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - DKColorTable.m - path - DKNightVersion/ColorTable/DKColorTable.m - sourceTree - <group> - - 3619A66CEAF955C359CF25CBCC468ADE - - buildActionMask - 2147483647 - files - - AA58D3EEFCBED00AA256325D80519762 - E986975088E6552CE4CCEE96762A0703 - 80785EE671A234F497F9D7B458D02223 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 366A613AE07EAA35B5789F6123FB48AF - - fileRef - F028843238E0416459CDD5317A21B779 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 372BFBF6F650A94B122131510FF774E0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIScrollView+MJRefresh.h - path - MJRefresh/UIScrollView+MJRefresh.h - sourceTree - <group> - - 3751D4A2116620F86DA90FCB7C8D82FF - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - FMDB.xcconfig - sourceTree - <group> - - 37BA0D6FAA1EF073108C8D0F05CC1EFA - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - MJExtension-dummy.m - sourceTree - <group> - - 384DC68188D48D502A3D96C78153EA2F - - fileRef - 3E3099179A3781D7D7F1BFFBE456C1FF - isa - PBXBuildFile - - 38748A5FF88F5C0A05FEED150DAC16DB - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshHeader.h - path - MJRefresh/Base/MJRefreshHeader.h - sourceTree - <group> - - 38E2982FE0D63199D0352B395E280FA3 - - fileRef - C39A747A22EF6ADB1D6E1991564E209C - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 38EEA9ED6B922946C54AD6BAC93B73AD - - fileRef - 94C0AFE3CCAC04FA079F403F13C055ED - isa - PBXBuildFile - - 390107B74A8C6B534B6A4EB9B296130F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SVRadialGradientLayer.h - path - SVProgressHUD/SVRadialGradientLayer.h - sourceTree - <group> - - 3912F979B38D666625239EFC7A5198E0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - DKImage.m - path - DKNightVersion/Core/DKImage.m - sourceTree - <group> - - 392C57DDFB9BFE4D900621FCE8EBB201 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - MJRefresh-prefix.pch - sourceTree - <group> - - 3939531EB270B4145C7ADFBAB2E5695F - - fileRef - FB46A967F3DDE62480467BEBCE75607C - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 397A0C6DE4CB5A0A5A2C1BDAD0252FF1 - - fileRef - 9458AE7D0741B651D9D246E907E6C0EB - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 3989E7406CED91A29F1CB2841D65B63B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshHeader.m - path - MJRefresh/Base/MJRefreshHeader.m - sourceTree - <group> - - 39B2562FBF3E995AB7EF8EC0A2DDDC34 - - children - - D6FFC7B986E04B23B6B9E98723DE63D8 - D53A2246773AC2B14A181B7A01EF8BBD - 4D5A2C6203C1CEF3E090BB27DCD26D36 - C3FB3AB6EB669A20014B966F1A3AA94F - 8F21E6405FBC780B74D6C6FA42C6D5C3 - D93E1B8D69476969600E3BE2442EC1C5 - 390107B74A8C6B534B6A4EB9B296130F - AF37E37112957F46A9484808F7E36DC5 - 587FA7C673E5B5E00D72CB96E5FD321D - 72472A6EF516E01400C4F54AC0FE9135 - - isa - PBXGroup - name - SVProgressHUD - path - SVProgressHUD - sourceTree - <group> - - 3AC899FD215914230EB9D8B57AD910D0 - - buildConfigurations - - 32508CD71389B6557800253ED79045E6 - DE39928A2ABC2213A0C10FA1E1CB52B3 - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 3ADA21861EA3F0D14344E401CB2CFFFC - - fileRef - 4B23A701F63434F5D75BB2E66D85DAA8 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 3B5BBA7F1E4CBCB75601F86E9B2CAC07 - - isa - PBXFileReference - lastKnownFileType - wrapper.framework - name - MobileCoreServices.framework - path - Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/MobileCoreServices.framework - sourceTree - DEVELOPER_DIR - - 3BAE3B8E3307FA71ED9AAC34CB0DDA2F - - children - - 272D2CAB1752F0D1722D5C966DF7DF4F - 1C8F38CF850A15721D65BF688AB350C2 - EE985FC4FFB7CB8D5CAB3C64C18AA2E3 - CA2C0447445BCAC7239897F846069EFB - 5B2724ADEAAEE9110491118E803665E2 - CFEC0EB589B90099B25BF510F4AB93C0 - AE8D8205DAE7034FBBAA118119C1BA8A - 7D204CC6422F92FB62FF0384DAEE8A83 - 94FF112EB3BB37E6BE359796B4D68C1F - B8A4F5CBA9FF98AD3C0B5595DAE56278 - D91EA5FAD7A935F7FE38A0D7167A1C50 - - isa - PBXGroup - name - standard - sourceTree - <group> - - 3C07EBBB796B6A49E82ADDF080D203E1 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libNJKWebViewProgress.a - path - libNJKWebViewProgress.a - sourceTree - BUILT_PRODUCTS_DIR - - 3C94E0D5ED31DC71CA979DAE02211998 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SDImageCache.h - path - SDWebImage/SDImageCache.h - sourceTree - <group> - - 3CA795C0836DC5D571CC4D6853CEEAFC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPCustomAnimation.h - path - pop/POPCustomAnimation.h - sourceTree - <group> - - 3CB0EC679B1D2007E558221B728BD4E7 - - children - - DC5AA875B416F9168CCBE529560EDE1F - 3E3099179A3781D7D7F1BFFBE456C1FF - BA62DF0497C88BDDFB7973546198E641 - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/SDWebImage - sourceTree - <group> - - 3CC75902D300A05FFC9E51AC41905A0C - - baseConfigurationReference - DD24BDCB6C8D8D64E71EC51553F143BA - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MACH_O_TYPE - staticlib - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PODS_ROOT - $(SRCROOT) - PRODUCT_NAME - $(TARGET_NAME) - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - 3D0901C2A4AB851402CDA86A62506F55 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - NSString+MJExtension.m - path - MJExtension/NSString+MJExtension.m - sourceTree - <group> - - 3D09452A09A763F78EA561CACE303DF7 - - baseConfigurationReference - 28A55EC5036EDF3C43CE79A6F884BE7A - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/MJExtension/MJExtension-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - 3D0F69C4CA35EB6A1F686BA88B19B4F6 - - fileRef - D451BC77C4A67866A807AFD37958C517 - isa - PBXBuildFile - - 3D32356147D239362A3FF7DCAB918205 - - buildConfigurations - - 7E9A67AE8C4941B71EBA1854861BC552 - 27EAC25242A69C58BBF8A63D030D30CB - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 3E3099179A3781D7D7F1BFFBE456C1FF - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - SDWebImage-dummy.m - sourceTree - <group> - - 3EBB8C52D5B84F0145C7B4843E6D8196 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SDWebImageDownloaderOperation.h - path - SDWebImage/SDWebImageDownloaderOperation.h - sourceTree - <group> - - 3EDADE731538CC87185FE9D8564CF17D - - buildActionMask - 2147483647 - files - - 665BDA8E623E013B24BDB89E75C05DD8 - 4D962F90A2C19895F4D3A59A946CAC8E - 132B1F6F7709C80586D8D64312135193 - 57A27DFF6132DC8EB83E26182235A75F - 60EDE20A50A44C3B7EFADC2D49851131 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 3EFC21A0B4DE4EF730F826C7C7A1DC75 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIBarButtonItem+Night.m - path - DKNightVersion/UIKit/UIBarButtonItem+Night.m - sourceTree - <group> - - 3F58ED32A9E20FC74FD6793A453E5E70 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshBackNormalFooter.h - path - MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h - sourceTree - <group> - - 3FB139F6D9B19151F259D5C8F3914AE5 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimationExtras.h - path - pop/POPAnimationExtras.h - sourceTree - <group> - - 40A3665D2BDD3A5083215B93B2B635A6 - - includeInIndex - 1 - isa - PBXFileReference - name - POPAnimation.mm - path - pop/POPAnimation.mm - sourceTree - <group> - - 40EBE04B5CFD7B6E55D09B6A0DEB3F2B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - DACircularProgressView.m - path - DACircularProgress/DACircularProgressView.m - sourceTree - <group> - - 41872B66CA6D947F4A3386450AC8F7EE - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIPageControl+Night.h - path - DKNightVersion/UIKit/UIPageControl+Night.h - sourceTree - <group> - - 41879D4AAE5C462EB8817412392E73D7 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - DKNightVersion.h - path - DKNightVersion/DKNightVersion.h - sourceTree - <group> - - 420E44B31095ED6C5B4CCF417C37EC23 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPSpringSolver.h - path - pop/POPSpringSolver.h - sourceTree - <group> - - 42B719963FEBB098AAE21B4AD83CE5EF - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - CoreAnimation+Night.h - path - DKNightVersion/CoreAnimation/CoreAnimation+Night.h - sourceTree - <group> - - 42C0A6866D491E89F43CF1FA2BD0033F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UITextField+Night.m - path - DKNightVersion/UIKit/UITextField+Night.m - sourceTree - <group> - - 43098B12DF5546CB576986E68DBCAFCA - - fileRef - 0B7448F84E1B4C725EE8C8A30AE4C1BF - isa - PBXBuildFile - - 4330562BA1A299CBD37F4602B1A4C95A - - fileRef - FC88064A37B95252C5C527F0647AFA69 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 43365D6EC5F9FCDE3E59A97EDEBFC1E7 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - SVProgressHUD-prefix.pch - sourceTree - <group> - - 43BA28469D72B79E4E79BFC6AE7328E9 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIButton+WebCache.h - path - SDWebImage/UIButton+WebCache.h - sourceTree - <group> - - 444602F3A422E5EC1DA8EF2A3DFA01EB - - fileRef - 0CB3C59B22A9FC7BC0629E03825BE0C3 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 445E1D26937A0AB389327E69E4E1D449 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimatableProperty.h - path - pop/POPAnimatableProperty.h - sourceTree - <group> - - 4497F356CDA07B41213339D445FEDEFD - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIView+MJExtension.m - path - MJRefresh/UIView+MJExtension.m - sourceTree - <group> - - 44F50504254959A81B2E96AD6261B2A9 - - fileRef - ACBD8DCB5A83BEC4D347964EE483633A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 453A9B33EF195D816518AAEE3FAAC0DF - - children - - 2C57CC0E4887851E2CBD72D0C00A7BAD - 49BB61DCFBF6E5D2E904E8E555B99B7E - 69B95498ADC113EAFBA3E42C521212D1 - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/DACircularProgress - sourceTree - <group> - - 474A8123146C16382C7A0C078E0EDBB0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJFoundation.m - path - MJExtension/MJFoundation.m - sourceTree - <group> - - 4971A04F98CFD85521F4B370848F948D - - children - - 5C371731BC6DED18605E747DD61B92A3 - - isa - PBXGroup - name - Targets Support Files - sourceTree - <group> - - 499C9F2CD935BFA28C90DB4923003D55 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - AFURLRequestSerialization.m - path - AFNetworking/AFURLRequestSerialization.m - sourceTree - <group> - - 49BB61DCFBF6E5D2E904E8E555B99B7E - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - DACircularProgress-dummy.m - sourceTree - <group> - - 4AA3B53FB26C1415A7F72CE14EE3D121 - - children - - 23253C4CF8128FBE390F87443BD6562C - 8754957EFA28F9A841D24331DFF3D3B5 - 091C6AC479BB1B465BF293191BA4EB90 - 3B5BBA7F1E4CBCB75601F86E9B2CAC07 - 358C18D56505647A3BFBAE84A7B11F5A - 7651A2978E64E886CE7A622F6F807ACC - 1A5C4AB96ED7AE020BE2FBABF668A88E - - isa - PBXGroup - name - iOS - sourceTree - <group> - - 4AA4EC14BE538143AA9A6074CA3DCEBE - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPSpringAnimation.h - path - pop/POPSpringAnimation.h - sourceTree - <group> - - 4B23A701F63434F5D75BB2E66D85DAA8 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - EXTKeyPathCoding.h - path - DKNightVersion/extobjc/EXTKeyPathCoding.h - sourceTree - <group> - - 4BA19F7DD8F9FDA648137B2CE89E7588 - - fileRef - 16BE2CD67995E70CB610D3E6C3F59356 - isa - PBXBuildFile - - 4BB3AD61E5E3DB2132D62C60DE2CF52D - - fileRef - D30B6A697C4F0DD87A2A674D2C470190 - isa - PBXBuildFile - - 4C61B999DF40B1B00449A7AFCBB61D5E - - fileRef - 9AEAC543BAB904679252AB4156F793DA - isa - PBXBuildFile - - 4D5A2C6203C1CEF3E090BB27DCD26D36 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SVProgressAnimatedView.h - path - SVProgressHUD/SVProgressAnimatedView.h - sourceTree - <group> - - 4D962F90A2C19895F4D3A59A946CAC8E - - fileRef - C3FB3AB6EB669A20014B966F1A3AA94F - isa - PBXBuildFile - - 4D9F1AA82224FB855E013A5F56B17A1C - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - DACircularProgressView.h - path - DACircularProgress/DACircularProgressView.h - sourceTree - <group> - - 4DB59CEFDCAB10EE888132C34E5A49AF - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJExtensionConst.h - path - MJExtension/MJExtensionConst.h - sourceTree - <group> - - 4F383980D527FBF4E1E59446EBDCABE4 - - includeInIndex - 1 - isa - PBXFileReference - name - POPCGUtils.mm - path - pop/POPCGUtils.mm - sourceTree - <group> - - 4F42E71491331FB254473FBD707274A3 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIView+Night.m - path - DKNightVersion/UIKit/UIView+Night.m - sourceTree - <group> - - 4FB4A85823C5A9D8B59B81C80CA3CF78 - - fileRef - 7188F0957099741427D36245A6E54FB6 - isa - PBXBuildFile - - 50169624A9225BE7EBCCE02910B6C40B - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libDKNightVersion.a - path - libDKNightVersion.a - sourceTree - BUILT_PRODUCTS_DIR - - 508C962B3E08E8DA04956BF349A3CDFD - - buildActionMask - 2147483647 - files - - 2053D71CF543C3A3CC47C5409D61923C - 6083F51989A0009C740D92767F4DC1FC - 00F09DB5E05D1208F27161E2F016FE36 - 022B549F10007177A43FAB1DD5114F7D - CE8965366A3B07A6C0615055A31B8B83 - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 51338B86242ECA265D4D1E29779CE5B7 - - fileRef - 30F4ACB997BBCFED40F26559F0C43C0E - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 51CD928E3D0955D5E7CD4A2D5D57C060 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - NJKWebViewProgress.m - path - NJKWebViewProgress/NJKWebViewProgress.m - sourceTree - <group> - - 520DCAEA28F613CEA694518D3199C2E4 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - MJRefresh.xcconfig - sourceTree - <group> - - 52779F8EC04ABF3285125604A009221B - - includeInIndex - 1 - isa - PBXFileReference - name - POPAnimatableProperty.mm - path - pop/POPAnimatableProperty.mm - sourceTree - <group> - - 53406FD23904169597858D7716FC7645 - - buildConfigurationList - D487D299A358CFFDB06F1A7782E02E0F - buildPhases - - 056F69DC67B5670E3D2B2DDF873B0850 - 25FDEF619A3B63C88174EE0599EFF736 - FD06763333C06C19A41242170C7CF713 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - pop - productName - pop - productReference - A895E55F6B119E7127FC0AE7B0EBEC70 - productType - com.apple.product-type.library.static - - 539FE091D2639896C01F77D746C8F553 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - NSObject+MJProperty.h - path - MJExtension/NSObject+MJProperty.h - sourceTree - <group> - - 53D2801BD1EA151DE56FD79DD7977A43 - - fileRef - F800B3FA3D41FC047B44DA896BDAC4FF - isa - PBXBuildFile - - 53FB0CBA14BE53F79F4EB4E7B46C523B - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - 54064CB8D622AFD47708AEE953D7860E - - buildConfigurations - - 98DF8819FD600C334C0DADE1C7A5371D - C47D45480990F48724C399B819C8BEF8 - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 542EB400B65BFF73EFD507BEDD30453C - - fileRef - 5E47F8D4425904467E8BF825C909EAB6 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 542FB07FE80287A647887B8ADAB8A460 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIImageView+WebCache.m - path - SDWebImage/UIImageView+WebCache.m - sourceTree - <group> - - 543D77F621A899DDD24AFC3E7F1DE129 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshBackGifFooter.m - path - MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m - sourceTree - <group> - - 54CE2C4624F292017FD23CDB86BA3FAD - - fileRef - A4C51863329640FB465DC94DCD400F8B - isa - PBXBuildFile - - 5524A68B285375333C8A6D653BE928E0 - - isa - PBXTargetDependency - name - SDWebImage - target - 63491E1EA0E903734AFD83F4D7DC319A - targetProxy - EA938873F4EA548167C14F3C7580FC33 - - 55D78C4B87AD2EB1128B1EF573DCA07E - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimator.h - path - pop/POPAnimator.h - sourceTree - <group> - - 55EAD85D305CED6AB67C325F4FEE5FA6 - - fileRef - 5AE4E235669EC113377519C46334089A - isa - PBXBuildFile - - 56A0276D3AB246AD2C65A0BCABD3B4DA - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJProperty.m - path - MJExtension/MJProperty.m - sourceTree - <group> - - 56D8735A9A14A1E09220E42B41D34792 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - NJKWebViewProgress-dummy.m - sourceTree - <group> - - 57A27DFF6132DC8EB83E26182235A75F - - fileRef - D93E1B8D69476969600E3BE2442EC1C5 - isa - PBXBuildFile - - 5831E31AABFC3799E1EB531F74DF33B4 - - fileRef - 42B719963FEBB098AAE21B4AD83CE5EF - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 585CDAC34F08FB220FF26FF55CA899B7 - - fileRef - 826A2C5B3DDEE0964B2980EA04A5A497 - isa - PBXBuildFile - - 586A4CC1BC5368A28EADD369C4559A73 - - buildConfigurations - - 189721A18CF2CD473CD4F76531C3BF3C - FE9B72C08EA457F2E2BF7CE320EE7BD9 - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 587B76A3E381AF7C3EFFDF6754D39288 - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - 587FA7C673E5B5E00D72CB96E5FD321D - - children - - 594CC0A9A50ED609E52DFCBA5119A172 - - isa - PBXGroup - name - Resources - sourceTree - <group> - - 58B011D090D06A83FAEC96E13AE87181 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIImage+MultiFormat.h - path - SDWebImage/UIImage+MultiFormat.h - sourceTree - <group> - - 58B71F76E619D1EDE3BA65D4540FD9D2 - - children - - C2C0D8D1C74ADF26572BE769258B867F - 8A6CECD5E6F11C618899B5BE8FB3F6D1 - 81EBE1997B57350F32D4C7A54E68CDFC - - isa - PBXGroup - name - NJKWebViewProgress - path - NJKWebViewProgress - sourceTree - <group> - - 58FBE86644C3E0DF6B287B17A9B1F996 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - DKNightVersionManager.h - path - DKNightVersion/Core/DKNightVersionManager.h - sourceTree - <group> - - 58FEA18A9A972E123E965073E1D32DE2 - - includeInIndex - 1 - isa - PBXFileReference - name - POPAnimationExtras.mm - path - pop/POPAnimationExtras.mm - sourceTree - <group> - - 59319D4046C5B66354582598A2E16177 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SDWebImageCompat.h - path - SDWebImage/SDWebImageCompat.h - sourceTree - <group> - - 594CC0A9A50ED609E52DFCBA5119A172 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - wrapper.plug-in - name - SVProgressHUD.bundle - path - SVProgressHUD/SVProgressHUD.bundle - sourceTree - <group> - - 599E2A0049425BF2D061F743BF057C31 - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - 5A4FF6B2DD2604820B26CF0E00ADFC20 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPSpringAnimationInternal.h - path - pop/POPSpringAnimationInternal.h - sourceTree - <group> - - 5ACDAB25349A840CFBFB034D5850BA52 - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - 8F74D9EA91F4C43190670066BAC37D44 - remoteInfo - MJRefresh - - 5AE4E235669EC113377519C46334089A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIToolbar+Night.m - path - DKNightVersion/UIKit/UIToolbar+Night.m - sourceTree - <group> - - 5AFC57DB29B71A94CDD967D606771F8C - - includeInIndex - 1 - isa - PBXFileReference - name - POPBasicAnimation.mm - path - pop/POPBasicAnimation.mm - sourceTree - <group> - - 5B2724ADEAAEE9110491118E803665E2 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - FMDatabasePool.h - path - src/fmdb/FMDatabasePool.h - sourceTree - <group> - - 5BA98B517FE220F0B5FFC21BF1D6D0F1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POP.h - path - pop/POP.h - sourceTree - <group> - - 5C217FF8A1665FCC43504CF1EF23C2F5 - - includeInIndex - 1 - isa - PBXFileReference - name - POPSpringAnimation.mm - path - pop/POPSpringAnimation.mm - sourceTree - <group> - - 5C371731BC6DED18605E747DD61B92A3 - - children - - 7AFA73332269E8957095F30B510145B1 - 61270B46896059B75A56E353EA7900F1 - AC5590B6E86B02889D07FB7A3716BD30 - 00B471635688CA88607CBEADA4AE8FB6 - 9636F59675415FFEDC6FD78077DEADEF - DD24BDCB6C8D8D64E71EC51553F143BA - 8B2CF98858F9F777E4DA294A52108BA0 - - isa - PBXGroup - name - Pods-TTNews - path - Target Support Files/Pods-TTNews - sourceTree - <group> - - 5CBFDC026AAED77902F3CAC48A96FE69 - - fileRef - 72D4CC2FF0C9A03F51AAA76DAB527961 - isa - PBXBuildFile - - 5CD308414EC21218DF0AFB748B815ACD - - includeInIndex - 1 - isa - PBXFileReference - name - POPCustomAnimation.mm - path - pop/POPCustomAnimation.mm - sourceTree - <group> - - 5D457827BFE445AF613A559D457750CE - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIWebView+AFNetworking.h - path - UIKit+AFNetworking/UIWebView+AFNetworking.h - sourceTree - <group> - - 5D52DB6DF2E6C24CECB0015A9177A2E1 - - fileRef - D7494EBF206671A3A2287926FAC95709 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 5E47F8D4425904467E8BF825C909EAB6 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UISlider+Night.h - path - DKNightVersion/UIKit/UISlider+Night.h - sourceTree - <group> - - 5E8E570F4C8B8B59F1FE6BC21DF7DB3F - - fileRef - EA9CE32C3F1212B31314975B53BBD645 - isa - PBXBuildFile - - 5E9E061F72D60420837253E9FC99040D - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - NSObject+Night.h - path - DKNightVersion/Core/NSObject+Night.h - sourceTree - <group> - - 5EFCB498297B17E7E3AB95266C9614D6 - - fileRef - 2036F6B0B797484514C2799195C825F4 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 5F4AECEF356E35995112F9167B219AD6 - - fileRef - 49BB61DCFBF6E5D2E904E8E555B99B7E - isa - PBXBuildFile - - 5FBA673E534DD7485FD75764258C8046 - - fileRef - 7707C2CA20EB25819E97181E23993FAE - isa - PBXBuildFile - - 6083F51989A0009C740D92767F4DC1FC - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - 60C1CF7C667D118176C1D53B6F8D1EE9 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - pop-prefix.pch - sourceTree - <group> - - 60EDE20A50A44C3B7EFADC2D49851131 - - fileRef - AF37E37112957F46A9484808F7E36DC5 - isa - PBXBuildFile - - 61270B46896059B75A56E353EA7900F1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.plist.xml - path - Pods-TTNews-acknowledgements.plist - sourceTree - <group> - - 6146D3EDA291EDE0D05EADE5E305664A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPMath.h - path - pop/POPMath.h - sourceTree - <group> - - 619B0B2F7ACD42F8EF700EAE6D1C95DA - - buildConfigurations - - A20A8D5F5488C64E37B6B88FD84647BD - D4EBADFA6A60CCED11FDD6595C3770B0 - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 61E82DD9438246B84050F9C5D7C5DFD6 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshNormalHeader.m - path - MJRefresh/Custom/Header/MJRefreshNormalHeader.m - sourceTree - <group> - - 620212E8B0A175CA61A7D8B96D81731A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIView+MJExtension.h - path - MJRefresh/UIView+MJExtension.h - sourceTree - <group> - - 62126C32C36B290BA4505C77F52C6155 - - fileRef - 6E5E48F0FA2C24795C8867EB41D56EB9 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 625911E7714259492D59783AAF218FD7 - - fileRef - 80EE313B7591686CDA199AADE3103760 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 62A93E37E2CDDFE62E120416E352BA01 - - includeInIndex - 1 - isa - PBXFileReference - name - DKColorTable.txt - path - DKNightVersion/ColorTable/DKColorTable.txt - sourceTree - <group> - - 63491E1EA0E903734AFD83F4D7DC319A - - buildConfigurationList - 54064CB8D622AFD47708AEE953D7860E - buildPhases - - D1FFF23830CEBBF7B04A425E757F39AF - A663DDF3607645CBEEEF2468FD2F4395 - DE7CCFD559106710B274D114A9841632 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - SDWebImage - productName - SDWebImage - productReference - AEDD7A8D8D423118CC2B1C97D6026850 - productType - com.apple.product-type.library.static - - 65E2CD5CF590B2E17E78A75E17D8F309 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIImageView+Night.m - path - DKNightVersion/Manual/UIImageView+Night.m - sourceTree - <group> - - 663713375D1E7919975E5E96F6A82802 - - isa - PBXTargetDependency - name - DACircularProgress - target - 9775596B3CA16905FC8F66DA8E2A3C67 - targetProxy - AB60DE99B9805BCAABF0A973CA318225 - - 665BDA8E623E013B24BDB89E75C05DD8 - - fileRef - D53A2246773AC2B14A181B7A01EF8BBD - isa - PBXBuildFile - - 6843FEBC1B25835C97E9E3B8558EA34B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIPageControl+Night.m - path - DKNightVersion/UIKit/UIPageControl+Night.m - sourceTree - <group> - - 68BCD15D5071D2B62D83B47D9772E23C - - fileRef - 6C13713E9ACF79B1C9DD83BB5294A0F4 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 69B95498ADC113EAFBA3E42C521212D1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - DACircularProgress-prefix.pch - sourceTree - <group> - - 6AFF341D5C4726BF24BDDB6C1446C591 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshBackFooter.h - path - MJRefresh/Base/MJRefreshBackFooter.h - sourceTree - <group> - - 6B34096B18606A9463DB65EF463DEAF4 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UITabBar+Night.h - path - DKNightVersion/UIKit/UITabBar+Night.h - sourceTree - <group> - - 6B6E84DDC7EF101B3A8A30CDBC74FC85 - - buildActionMask - 2147483647 - files - - D316AA0D2FBBFF2EBA6F1E8B7030E86F - 5831E31AABFC3799E1EB531F74DF33B4 - 27987FFC8A7521500DDDFF1913E34AF6 - D851FF3699E93DE46793E401B23C3636 - F0019A36DAA2072760CE5C16C76DF095 - BCA89B9A0551A9AB6D26A089A419C6ED - FADDFEF3FAD9AD71AAC4D802C69E7980 - 9EAEE9ABAF59D8090998CC670AED916D - 3ADA21861EA3F0D14344E401CB2CFFFC - 92329917B3C6E48A57A23FAE8D6CB755 - E7A197C569F37AFB32DB951DEE7EEF5F - 6F5A0356163996974E2C4C19AA3DA31D - D83210EEE85AF00991EE70C7B579D939 - D86281719FC4F4C2953908F433CC3CF2 - 5EFCB498297B17E7E3AB95266C9614D6 - 62126C32C36B290BA4505C77F52C6155 - 79965FF6186FA9F8BEF12DB9F6E3CEAD - 0027C71767B2F5C6007AB990ED6E0F35 - 00BA2602AC2FFE49870F2D1C58836452 - 087DD8E87123B73129EC27879DC1CB09 - 03DE154CF25C8F8E64787FD66CAC26F0 - 08DA05B63C2B0C5F9C392BDCA39979FA - 542EB400B65BFF73EFD507BEDD30453C - 1F54C462201DF87BDD390BF44DDBC9FB - FA031FE32C384AF41A819924ECC0589E - B9935BDB8DB6AB67BC4DD470495C3C28 - 29DCD5202C4BAA080E0AEA92548F16CE - 863030510BF766FEEA9DC9EC0CC6131C - 397A0C6DE4CB5A0A5A2C1BDAD0252FF1 - D48BAC96BAD2B109BEFC35BCEFF69FFD - 9DE2F6C888E085C7AA47BE05E19E4C25 - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 6C13713E9ACF79B1C9DD83BB5294A0F4 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPCGUtils.h - path - pop/POPCGUtils.h - sourceTree - <group> - - 6CF9B693754B004AD7FB2F8A6C0C8F36 - - fileRef - BB5F7E48CF4B7CB1DE4B01F6CC5B876A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 6E5E48F0FA2C24795C8867EB41D56EB9 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIImageView+Night.h - path - DKNightVersion/Manual/UIImageView+Night.h - sourceTree - <group> - - 6E80858B842657D04234289E938017AC - - fileRef - 3F58ED32A9E20FC74FD6793A453E5E70 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 6EC97BDB3FC508526F9E388095A7DDCC - - fileRef - F0DB9199DC083A489978BFDE24F78183 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 6EE360D085C7EDD97B0A48A415012D37 - - fileRef - 89A36A1198F55774A94BC42AD208D3AF - isa - PBXBuildFile - - 6F5A0356163996974E2C4C19AA3DA31D - - fileRef - 5E9E061F72D60420837253E9FC99040D - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 6FAE6E76F74AA7218C7A5976A9A4FC9F - - baseConfigurationReference - 8B2CF98858F9F777E4DA294A52108BA0 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MACH_O_TYPE - staticlib - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PODS_ROOT - $(SRCROOT) - PRODUCT_NAME - $(TARGET_NAME) - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - 7010A5470E849DDCF43C386E942E2802 - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - 704F52D98420017798AE29B7E9E576D3 - - fileRef - BD4C5BE0A37EB2CD33DF2BE21B98B9B9 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 709FD2FD0C8CDBECB9AB43F16DC72B70 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshConst.h - path - MJRefresh/MJRefreshConst.h - sourceTree - <group> - - 710DA5E9C44A1BC56CD13FC1F2D6E099 - - fileRef - 474A8123146C16382C7A0C078E0EDBB0 - isa - PBXBuildFile - - 711B8F0BD15514F946EB73680D6E07E5 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - NJKWebViewProgress.xcconfig - sourceTree - <group> - - 713AA1BADCB8D73F03918F5A85A608D0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - AFHTTPSessionManager.h - path - AFNetworking/AFHTTPSessionManager.h - sourceTree - <group> - - 7188F0957099741427D36245A6E54FB6 - - includeInIndex - 1 - isa - PBXFileReference - name - POPMath.mm - path - pop/POPMath.mm - sourceTree - <group> - - 71C731B37D2EA9A02A3DA8088CA67AE9 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPDecayAnimationInternal.h - path - pop/POPDecayAnimationInternal.h - sourceTree - <group> - - 72092F11D1E8E9855A5E157C8BB11813 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UISlider+Night.m - path - DKNightVersion/UIKit/UISlider+Night.m - sourceTree - <group> - - 72472A6EF516E01400C4F54AC0FE9135 - - children - - 9446782CA90A06C472946A60B765C77C - D2259CB60A2F9266A9285E57A74AC735 - 43365D6EC5F9FCDE3E59A97EDEBFC1E7 - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/SVProgressHUD - sourceTree - <group> - - 72645AB555ACB154314B9E0E078A93C1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UISearchBar+Night.h - path - DKNightVersion/UIKit/UISearchBar+Night.h - sourceTree - <group> - - 72A46D3074C7025978DFFC986BAA8558 - - children - - 62A93E37E2CDDFE62E120416E352BA01 - - isa - PBXGroup - name - Resources - sourceTree - <group> - - 72D4CC2FF0C9A03F51AAA76DAB527961 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshStateHeader.m - path - MJRefresh/Custom/Header/MJRefreshStateHeader.m - sourceTree - <group> - - 740482D4A2AE34227F9413D08B166514 - - fileRef - 5AFC57DB29B71A94CDD967D606771F8C - isa - PBXBuildFile - - 744B5A1002CD322D063CCD4A71AF2B7D - - fileRef - CC180EE0FCC1779DBDAEB2D74C4233A3 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 747242460B935FEA3E994652BD8C2E0B - - buildActionMask - 2147483647 - files - - 7010A5470E849DDCF43C386E942E2802 - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 74E3F11060C970BF46AC486E730E76C8 - - children - - FC393A9B89491596F3BF5B6F7702B70A - EFF6557ADDB96E851B58DB7774E0A797 - 42B719963FEBB098AAE21B4AD83CE5EF - - isa - PBXGroup - name - CoreAnimation - sourceTree - <group> - - 755762CB5409A3179B707E034F5B138B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - NSObject+DeallocBlock.m - path - DKNightVersion/DeallocBlockExecutor/NSObject+DeallocBlock.m - sourceTree - <group> - - 7565AA497FF696CD274710A58EB664C2 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIRefreshControl+AFNetworking.m - path - UIKit+AFNetworking/UIRefreshControl+AFNetworking.m - sourceTree - <group> - - 75A8688F5F25EF68368B87A723900CEF - - isa - PBXTargetDependency - name - FMDB - target - 9F12FB4A63E20F601CFB2E64A65B57C1 - targetProxy - 7E6E83AF0997C5A2FBD11BD44528E978 - - 75A8FFD8DE5318E898CB3C5D6F0D80F2 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - AFImageDownloader.m - path - UIKit+AFNetworking/AFImageDownloader.m - sourceTree - <group> - - 760C261E5F5A81992095283D02FF410B - - fileRef - 42C0A6866D491E89F43CF1FA2BD0033F - isa - PBXBuildFile - - 7651A2978E64E886CE7A622F6F807ACC - - isa - PBXFileReference - lastKnownFileType - wrapper.framework - name - Security.framework - path - Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/Security.framework - sourceTree - DEVELOPER_DIR - - 766D31A079D0044C43AE2B4D9F4E0DBE - - fileRef - 4D5A2C6203C1CEF3E090BB27DCD26D36 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 7707C2CA20EB25819E97181E23993FAE - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshAutoStateFooter.m - path - MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m - sourceTree - <group> - - 7798C71F1A8F565EF97463695B24952C - - fileRef - BE84F592718A7860CA775F488A519E13 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 77CF2A7107BC61A2E90025871305961D - - buildConfigurations - - 162B149B0DD54501D7C75154E5B604EE - 0EB5E89BB17510AE9D4971A42147A390 - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 77E1C31349DC16065E3C5C6DB868336D - - children - - 3BAE3B8E3307FA71ED9AAC34CB0DDA2F - D20C3F518C8BEEF7CA31DF91F577FD73 - - isa - PBXGroup - name - FMDB - path - FMDB - sourceTree - <group> - - 77F797A5A72065EE7351CF41728C2EC3 - - fileRef - 620212E8B0A175CA61A7D8B96D81731A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 780CB4C63BE3C7160E89458A92D284D7 - - isa - PBXTargetDependency - name - MJRefresh - target - 8F74D9EA91F4C43190670066BAC37D44 - targetProxy - 5ACDAB25349A840CFBFB034D5850BA52 - - 78940A6980F359C4304C695EF6E67C5D - - fileRef - F6B2DFB3A0EE9FD880C60B13DEBE47ED - isa - PBXBuildFile - - 78BC37B580ACA8D2F846A64B667E5A46 - - fileRef - 542FB07FE80287A647887B8ADAB8A460 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 790FC674679ACC231DED3229818F4B7D - - fileRef - C8FE1703C2FE4E4FCE270AB64D41C43B - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 7918D61FAAD756712F39EB9EEDB1033E - - baseConfigurationReference - 9446782CA90A06C472946A60B765C77C - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/SVProgressHUD/SVProgressHUD-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - 79965FF6186FA9F8BEF12DB9F6E3CEAD - - fileRef - 81292F3844994954D696E2FB938508BE - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 7A4FC2348D43AB90D1E85D6D949862F6 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIImageView+AFNetworking.m - path - UIKit+AFNetworking/UIImageView+AFNetworking.m - sourceTree - <group> - - 7AFA73332269E8957095F30B510145B1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text - path - Pods-TTNews-acknowledgements.markdown - sourceTree - <group> - - 7C1D52E39E6EFCD821AC6ED8D9F7FF80 - - children - - 8FB3C14E9AA647EFCF537833D7CDA965 - FC7245612AF6EF9AFC848BB37D4BE804 - 9A378E3FA547A1DCF0276D412B8B0107 - 35AED4E04DFD8E1B002A1CE8D48851FC - 241AFDF1A45B8F607AB3201F5B4B8D9D - 3912F979B38D666625239EFC7A5198E0 - 58FBE86644C3E0DF6B287B17A9B1F996 - 89F62DE73CDD7C230F1973591A56ACCA - 5E9E061F72D60420837253E9FC99040D - 863E932B2A1BFCA7D0C139DAEC6E2141 - B3A9C89E1187A533CB4FF22471C7A0E3 - 04EE7FD0A4668B0BF15D595695752EF5 - - isa - PBXGroup - name - Core - sourceTree - <group> - - 7C1F8789E40EFE0223E50570FC68930E - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshGifHeader.h - path - MJRefresh/Custom/Header/MJRefreshGifHeader.h - sourceTree - <group> - - 7D0852C548A592E2898D99287F864873 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SDWebImageDecoder.m - path - SDWebImage/SDWebImageDecoder.m - sourceTree - <group> - - 7D1A75A73B29D589B3596C8C127AB4BD - - fileRef - 65E2CD5CF590B2E17E78A75E17D8F309 - isa - PBXBuildFile - - 7D204CC6422F92FB62FF0384DAEE8A83 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - FMDatabaseQueue.m - path - src/fmdb/FMDatabaseQueue.m - sourceTree - <group> - - 7D865DF7D635C708FFE394C76B4650F4 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - DKNightVersion-prefix.pch - sourceTree - <group> - - 7DB346D0F39D3F0E887471402A8071AB - - children - - BA6428E9F66FD5A23C0A2E06ED26CD2F - F4CDA5FA9197A41E0081E84F932906EB - B1BAAA38911400E648E59090A53A3A1D - 930323DB8B63DAFC99CD413F47DC01A1 - 4971A04F98CFD85521F4B370848F948D - - isa - PBXGroup - sourceTree - <group> - - 7DB89CAFE4A77D0845E3A529F4C33236 - - includeInIndex - 1 - isa - PBXFileReference - name - POPAnimationTracer.mm - path - pop/POPAnimationTracer.mm - sourceTree - <group> - - 7DE4BBEBD3DAA84CE18482CC6598F7D0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - AFURLSessionManager.m - path - AFNetworking/AFURLSessionManager.m - sourceTree - <group> - - 7DE8F673DA8EB314243E55B2D0905B53 - - isa - PBXTargetDependency - name - NJKWebViewProgress - target - B4A3E08D49931371F7247521A50EAF79 - targetProxy - A174822E2D67CE8E0448F1C5D1FAC183 - - 7E20A38F3E9ADB2BC380D038FACD345E - - children - - 4D9F1AA82224FB855E013A5F56B17A1C - 40EBE04B5CFD7B6E55D09B6A0DEB3F2B - 0C068AB4DD5B93B3AEAF97DCF2B4E61F - E9C94F3B1FCC268F06C8A599A810D60F - 453A9B33EF195D816518AAEE3FAAC0DF - - isa - PBXGroup - name - DACircularProgress - path - DACircularProgress - sourceTree - <group> - - 7E6E83AF0997C5A2FBD11BD44528E978 - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - 9F12FB4A63E20F601CFB2E64A65B57C1 - remoteInfo - FMDB - - 7E9A67AE8C4941B71EBA1854861BC552 - - baseConfigurationReference - 520DCAEA28F613CEA694518D3199C2E4 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/MJRefresh/MJRefresh-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - 7EA9E907BE2F9926B8D7328C89B8F884 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UITableView+Night.h - path - DKNightVersion/UIKit/UITableView+Night.h - sourceTree - <group> - - 7ED9C88EEEDA2C98867CFBABBA1945DE - - fileRef - 2E6CF32A69836EB96102B6C209882BE2 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 7F0578A0258E02384F6229C678E60D31 - - fileRef - 7565AA497FF696CD274710A58EB664C2 - isa - PBXBuildFile - - 7F15E8A2BF4A27961B1BC7CA94FC3426 - - fileRef - 8CFB044742F1F636842E0564C79D3B66 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 80785EE671A234F497F9D7B458D02223 - - fileRef - 84645189CB7DC3D1421B7B2466ED5FE2 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 80865375BD7061D5F7CBB8DE19AC8233 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - wrapper.plug-in - name - MJRefresh.bundle - path - MJRefresh/MJRefresh.bundle - sourceTree - <group> - - 80EE313B7591686CDA199AADE3103760 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIImageView+HighlightedWebCache.m - path - SDWebImage/UIImageView+HighlightedWebCache.m - sourceTree - <group> - - 81292F3844994954D696E2FB938508BE - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UILabel+Night.h - path - DKNightVersion/UIKit/UILabel+Night.h - sourceTree - <group> - - 81EBE1997B57350F32D4C7A54E68CDFC - - children - - 711B8F0BD15514F946EB73680D6E07E5 - 56D8735A9A14A1E09220E42B41D34792 - 1B514CAC6F419732F611364C49453788 - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/NJKWebViewProgress - sourceTree - <group> - - 82364F956F965883ACC87278A945F771 - - fileRef - B736674E245CEF6684053B0233339ED7 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 8250CD8A70A80EBC4B93B705D50126B2 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshAutoStateFooter.h - path - MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h - sourceTree - <group> - - 826A2C5B3DDEE0964B2980EA04A5A497 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshGifHeader.m - path - MJRefresh/Custom/Header/MJRefreshGifHeader.m - sourceTree - <group> - - 82AC9861A9FE0BEFEC04D31C3B05ABB2 - - fileRef - 40EBE04B5CFD7B6E55D09B6A0DEB3F2B - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 82C22E73E0E73736F0D81DF58D56BF2A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIWebView+AFNetworking.m - path - UIKit+AFNetworking/UIWebView+AFNetworking.m - sourceTree - <group> - - 82DCD8837B1AFF60D5F4971CA28D606D - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - 82E466DC61BD55C67F8005E93FB5A125 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UINavigationBar+Animation.h - path - DKNightVersion/Manual/UINavigationBar+Animation.h - sourceTree - <group> - - 830050886B18F54E17F117FD8EFC717B - - fileRef - ECB865BC44644DA90479F9C206E382D0 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 8321302C0702383F917BADA569BC2B6A - - fileRef - 35AED4E04DFD8E1B002A1CE8D48851FC - isa - PBXBuildFile - - 83DC116EE7BD47DF97B31377AC71F19C - - fileRef - 445E1D26937A0AB389327E69E4E1D449 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 84645189CB7DC3D1421B7B2466ED5FE2 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - NJKWebViewProgressView.m - path - NJKWebViewProgress/NJKWebViewProgressView.m - sourceTree - <group> - - 84AF63084FFE4472EFC6131BD113B13D - - buildActionMask - 2147483647 - files - - 587B76A3E381AF7C3EFFDF6754D39288 - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 851D64F16B178959E32F16A169FDFCB6 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - pop.xcconfig - sourceTree - <group> - - 85F7BDA2650A545C7BD53DEBCEAEB714 - - fileRef - FC7245612AF6EF9AFC848BB37D4BE804 - isa - PBXBuildFile - - 85FAB6F1D2FE0598BF2E61D7BB084B8D - - fileRef - 027DAE81362CCFBA536EF587D6AF890B - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 860400C3A21CB2E154D074B974F8420C - - fileRef - 55D78C4B87AD2EB1128B1EF573DCA07E - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 863030510BF766FEEA9DC9EC0CC6131C - - fileRef - 1962B9F0C1051AECDA8B89D66CD291FC - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 863E932B2A1BFCA7D0C139DAEC6E2141 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - NSObject+Night.m - path - DKNightVersion/Core/NSObject+Night.m - sourceTree - <group> - - 8653A8CA11B7C06ED3B91869DA308E95 - - fileRef - 58B011D090D06A83FAEC96E13AE87181 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 86903773F3A1C526B318640363013935 - - fileRef - 8F21E6405FBC780B74D6C6FA42C6D5C3 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 86D3C675657C8AFE6F73E5D0F209E990 - - fileRef - A6DD5BD593CF7F708355BD477814A8AC - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 870CE7053EE55247E23C49C37448C82A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAction.h - path - pop/POPAction.h - sourceTree - <group> - - 8754957EFA28F9A841D24331DFF3D3B5 - - isa - PBXFileReference - lastKnownFileType - wrapper.framework - name - Foundation.framework - path - Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/Foundation.framework - sourceTree - DEVELOPER_DIR - - 881AE394748186FC66252043BB5EF412 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - NSObject+MJCoding.m - path - MJExtension/NSObject+MJCoding.m - sourceTree - <group> - - 88EFA6AC8C3D86F5E7FAB0E18FDE7C26 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libDACircularProgress.a - path - libDACircularProgress.a - sourceTree - BUILT_PRODUCTS_DIR - - 898AAE5FD2A76A7B287B4525A98119DF - - baseConfigurationReference - 711B8F0BD15514F946EB73680D6E07E5 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/NJKWebViewProgress/NJKWebViewProgress-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - 89A36A1198F55774A94BC42AD208D3AF - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshFooter.m - path - MJRefresh/Base/MJRefreshFooter.m - sourceTree - <group> - - 89F62DE73CDD7C230F1973591A56ACCA - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - DKNightVersionManager.m - path - DKNightVersion/Core/DKNightVersionManager.m - sourceTree - <group> - - 8A5CF3A2E5440A7D7B011B791D01029A - - fileRef - E2C595890A1C1D2634DFC6C56EEC8D19 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 8A6CECD5E6F11C618899B5BE8FB3F6D1 - - children - - DF3432DD7E06021EE8A3D9E83F920074 - 84645189CB7DC3D1421B7B2466ED5FE2 - - isa - PBXGroup - name - ProgressView - sourceTree - <group> - - 8AC81995D070E176D6151C42705E5FEB - - fileRef - 04571616A0774568EE308B94E9CA7ADE - isa - PBXBuildFile - - 8B2CF98858F9F777E4DA294A52108BA0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - Pods-TTNews.release.xcconfig - sourceTree - <group> - - 8B2D82357BE4C2A10AB2CD4724B8E654 - - buildActionMask - 2147483647 - files - - D0D5FFE53407F16C9CD3D0ECF43ACE93 - B418C9BDCF8C6BAC2E3942F157BB6D4C - 0F8A8E0D6FAEBF758E0B4D620335F094 - D0C5FF5485EAA2CDA03E9796B407697A - 313531CEDA0294311D87B4F4C3F073F4 - F977B243321331B58666E9284634101A - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 8B5158A4ABC68EBCE0EC603D1C4520D3 - - fileRef - F2A1B494417E8906E69CBCCC9377708F - isa - PBXBuildFile - - 8B5E66EEADBA561A5198CB206BD4D66F - - fileRef - FB8E6FDC31229DAA4D8F3B319D62E6D1 - isa - PBXBuildFile - - 8C3614DFB86BEE3C5D8AF4C440835FEC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPBasicAnimation.h - path - pop/POPBasicAnimation.h - sourceTree - <group> - - 8C641D918A23FE580FE044435953E8E8 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - NSObject+MJClass.h - path - MJExtension/NSObject+MJClass.h - sourceTree - <group> - - 8CBAC87BB0370982F1EA12173D15FFB4 - - fileRef - 72092F11D1E8E9855A5E157C8BB11813 - isa - PBXBuildFile - - 8CFB044742F1F636842E0564C79D3B66 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPPropertyAnimationInternal.h - path - pop/POPPropertyAnimationInternal.h - sourceTree - <group> - - 8D4BBA5A842DBAD4BC1AA0832751C3C4 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - AFURLResponseSerialization.m - path - AFNetworking/AFURLResponseSerialization.m - sourceTree - <group> - - 8DC2468DD9EC492AC88E48D891338E9D - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SDWebImageManager.m - path - SDWebImage/SDWebImageManager.m - sourceTree - <group> - - 8E6FEA50B8951C38D0598B79D0DDD30D - - includeInIndex - 1 - isa - PBXFileReference - name - POPAnimationEvent.mm - path - pop/POPAnimationEvent.mm - sourceTree - <group> - - 8E82178CC9BD8BE3DBF2C10E33807AEB - - fileRef - 499C9F2CD935BFA28C90DB4923003D55 - isa - PBXBuildFile - - 8F21E6405FBC780B74D6C6FA42C6D5C3 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SVProgressHUD.h - path - SVProgressHUD/SVProgressHUD.h - sourceTree - <group> - - 8F68B37533C5AC9C8588BD0449D32673 - - fileRef - 983909C97559B4E35B0A6454865591B0 - isa - PBXBuildFile - - 8F74D9EA91F4C43190670066BAC37D44 - - buildConfigurationList - 3D32356147D239362A3FF7DCAB918205 - buildPhases - - 346003011339A97DDE1A394DF0A3BB91 - F6AAF5593D0C5E24B3377E5C3999D0AA - 0B678E4E7D716E2C6EC64BDB96418C7E - - buildRules - - dependencies - - isa - PBXNativeTarget - name - MJRefresh - productName - MJRefresh - productReference - 2A16DA566AF62EF3721B19BA70B66319 - productType - com.apple.product-type.library.static - - 8FB3C14E9AA647EFCF537833D7CDA965 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - DKColor.h - path - DKNightVersion/Core/DKColor.h - sourceTree - <group> - - 904EC3379A9E226064CB03DC1ACA2EEA - - fileRef - 372BFBF6F650A94B122131510FF774E0 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 9059AC276AEAEE67CA6CD37776499A2F - - buildActionMask - 2147483647 - files - - 82DCD8837B1AFF60D5F4971CA28D606D - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 91CAC2B69903F5F3D7C25A95CA77A600 - - fileRef - 0A6A3B2BDB42B167BC55FE07F4D601BC - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 92177973EBA0B34751CF530522C8F73D - - fileRef - 2CD360EF74E638D4D88DCA0C002951CE - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 922ACEFE37E22A36A6F0209742B7B51A - - fileRef - C917B030D2D63B74FD5D309F008FAF8A - isa - PBXBuildFile - - 9230119085130B273ECF20F076081DA1 - - fileRef - AF20DCCA2D55DC0A9EF54380B1FAB4FF - isa - PBXBuildFile - - 92329917B3C6E48A57A23FAE8D6CB755 - - fileRef - A6D80A2219B8458E2D2DA26EEF530CA2 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 928353533005A4198EBDA5B700D37B64 - - buildConfigurationList - 77CF2A7107BC61A2E90025871305961D - buildPhases - - DE0BAD758C6F291CE7E10DA077987C62 - 508C962B3E08E8DA04956BF349A3CDFD - B981229135DBE9773A86BDB116C368D1 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - AFNetworking - productName - AFNetworking - productReference - F7131033FF45A01C50502DB74DDC8B35 - productType - com.apple.product-type.library.static - - 930323DB8B63DAFC99CD413F47DC01A1 - - children - - F7131033FF45A01C50502DB74DDC8B35 - 88EFA6AC8C3D86F5E7FAB0E18FDE7C26 - 50169624A9225BE7EBCCE02910B6C40B - D68361F633344D64600A8098AD885F95 - B834D7AC4A775B5387745E9EDD8C2CE6 - 2A16DA566AF62EF3721B19BA70B66319 - 3C07EBBB796B6A49E82ADDF080D203E1 - BF30D702D51DD1ED6EA45DFDB6E99923 - A895E55F6B119E7127FC0AE7B0EBEC70 - AEDD7A8D8D423118CC2B1C97D6026850 - CD67C244234E345DF392971565F69F21 - - isa - PBXGroup - name - Products - sourceTree - <group> - - 932430D4A2289A39BE7C50EEAA60246A - - fileRef - AB863E2FD26779958F545A74EDD20269 - isa - PBXBuildFile - - 93313A2703EA8405C961C7A4A93EBA00 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimatorPrivate.h - path - pop/POPAnimatorPrivate.h - sourceTree - <group> - - 9376D50310F2A816C6810D56C7431568 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIImageView+HighlightedWebCache.h - path - SDWebImage/UIImageView+HighlightedWebCache.h - sourceTree - <group> - - 9446782CA90A06C472946A60B765C77C - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - SVProgressHUD.xcconfig - sourceTree - <group> - - 9458AE7D0741B651D9D246E907E6C0EB - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UITextView+Night.h - path - DKNightVersion/UIKit/UITextView+Night.h - sourceTree - <group> - - 9490A1BF0EAAA5CDBE60731B62DFFA85 - - fileRef - C74208C0F7AE5568BAE95BB70E4889BC - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 94A5358AF7E02BD7D58E45AC1FE0AD3F - - fileRef - 40A3665D2BDD3A5083215B93B2B635A6 - isa - PBXBuildFile - - 94C0AFE3CCAC04FA079F403F13C055ED - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIProgressView+AFNetworking.m - path - UIKit+AFNetworking/UIProgressView+AFNetworking.m - sourceTree - <group> - - 94DF3A6264A6FDA5D4C1C563D39B3267 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - DKDeallocBlockExecutor.m - path - DKNightVersion/DeallocBlockExecutor/DKDeallocBlockExecutor.m - sourceTree - <group> - - 94FF112EB3BB37E6BE359796B4D68C1F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - FMDB.h - path - src/fmdb/FMDB.h - sourceTree - <group> - - 956897379B9772A06F53A667C14CB13D - - fileRef - B99088FC0CED9CF0C12C78E9A7FD5965 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 9610EB6DD09A8D2E0F53A06DF74A769C - - fileRef - 59319D4046C5B66354582598A2E16177 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 9636F59675415FFEDC6FD78077DEADEF - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.script.sh - path - Pods-TTNews-resources.sh - sourceTree - <group> - - 9655D06F88A9A5D7E1F2640BBD32DBC1 - - children - - 14271704DDA019ADD853EB2483AC858A - 2483254F23855294248B0307F56706FA - A2982E2A64283154B31B5E7106754649 - 75A8FFD8DE5318E898CB3C5D6F0D80F2 - FB46A967F3DDE62480467BEBCE75607C - 16BE2CD67995E70CB610D3E6C3F59356 - ECB865BC44644DA90479F9C206E382D0 - EBD492D43A3A67F231799ADEBD06BBCD - E790F4B99B3E4F056D90DE0A4A091DB1 - F6B2DFB3A0EE9FD880C60B13DEBE47ED - 2EB1364F6A10CE22A5B3DE8BB3DB5F74 - F0DB9199DC083A489978BFDE24F78183 - 7A4FC2348D43AB90D1E85D6D949862F6 - 027DAE81362CCFBA536EF587D6AF890B - D7494EBF206671A3A2287926FAC95709 - 94C0AFE3CCAC04FA079F403F13C055ED - 0B18F2E823D3FC778625EC66708088FB - 7565AA497FF696CD274710A58EB664C2 - 5D457827BFE445AF613A559D457750CE - 82C22E73E0E73736F0D81DF58D56BF2A - - isa - PBXGroup - name - UIKit - sourceTree - <group> - - 96F9E868F50D2514B84278D088A919D7 - - fileRef - 89F62DE73CDD7C230F1973591A56ACCA - isa - PBXBuildFile - - 9775596B3CA16905FC8F66DA8E2A3C67 - - buildConfigurationList - 619B0B2F7ACD42F8EF700EAE6D1C95DA - buildPhases - - A6993E0B001E6EB572176DFCF9B87C6A - F2A9AAC03867BB4990470BAC5912E030 - 1246A1C00980422020A6884856143651 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - DACircularProgress - productName - DACircularProgress - productReference - 88EFA6AC8C3D86F5E7FAB0E18FDE7C26 - productType - com.apple.product-type.library.static - - 981C847131F1E694A293E133DDE918DE - - fileRef - B9155B26E8C2310F99E505C74CE287A0 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 983909C97559B4E35B0A6454865591B0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - MJRefresh-dummy.m - sourceTree - <group> - - 9894E4DB291E5043EA8FC890F28E24C4 - - fileRef - 333665A0FC00013B7E8D903B093EB4DB - isa - PBXBuildFile - - 98DF8819FD600C334C0DADE1C7A5371D - - baseConfigurationReference - DC5AA875B416F9168CCBE529560EDE1F - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/SDWebImage/SDWebImage-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - 997CA834C83CB6BACC85AB9719CD28A3 - - fileRef - 1574985E68B39F87E1D366F6CA973F86 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 9A378E3FA547A1DCF0276D412B8B0107 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - DKColorTable.h - path - DKNightVersion/ColorTable/DKColorTable.h - sourceTree - <group> - - 9AEAC543BAB904679252AB4156F793DA - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJPropertyType.m - path - MJExtension/MJPropertyType.m - sourceTree - <group> - - 9B2BAE79E21A89F45EB6446993E9761E - - fileRef - 5CD308414EC21218DF0AFB748B815ACD - isa - PBXBuildFile - - 9B324DB188A8DAF715392A21B7786CE7 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SDWebImageOperation.h - path - SDWebImage/SDWebImageOperation.h - sourceTree - <group> - - 9B6303B955BA5B60BB4221B05D1609CF - - fileRef - F765039FCC5D5506BEC765EE2B818F69 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - 9BF8D708AC9892125B0AA4E540361FF8 - - fileRef - ABE7CAE4AAC89EE157FD4B84E8227453 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 9C739E488F9426EB75D8770B832A689E - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPDecayAnimation.h - path - pop/POPDecayAnimation.h - sourceTree - <group> - - 9CB70DDC67A0424818079CAB930357FB - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshBackStateFooter.h - path - MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h - sourceTree - <group> - - 9CD83411F46F2567211116F44269772D - - fileRef - E1D3E01D8670003AC52C24C77D2FA2CF - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 9D0426E9152828FA5C8778FE46D6AB80 - - fileRef - 3FB139F6D9B19151F259D5C8F3914AE5 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 9D2AA60CE2A57C9F5D79D9EF23108A5A - - fileRef - CD1ADC471EE4AA1560ED050C3D73EF73 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - 9DE2F6C888E085C7AA47BE05E19E4C25 - - fileRef - C4F8DC5157EC439A67321F96CFF9B7A8 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 9E20EB77864556B3E37D8511D3955C3E - - fileRef - 9B324DB188A8DAF715392A21B7786CE7 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 9EAB3E42EF957541E6C3C76D35FD008E - - buildConfigurationList - ECCC06F4F1551987F0834A094FDCB1CA - buildPhases - - 2CBFB844B4D7FEB4FF2F11CAE31CAD75 - 747242460B935FEA3E994652BD8C2E0B - F95AF419D96CDED026D79CDD7A3B3023 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - MJExtension - productName - MJExtension - productReference - B834D7AC4A775B5387745E9EDD8C2CE6 - productType - com.apple.product-type.library.static - - 9EAEE9ABAF59D8090998CC670AED916D - - fileRef - 58FBE86644C3E0DF6B287B17A9B1F996 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - 9F12FB4A63E20F601CFB2E64A65B57C1 - - buildConfigurationList - 3AC899FD215914230EB9D8B57AD910D0 - buildPhases - - 050B66E154969A68944942A22BD9B314 - 84AF63084FFE4472EFC6131BD113B13D - 8B2D82357BE4C2A10AB2CD4724B8E654 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - FMDB - productName - FMDB - productReference - D68361F633344D64600A8098AD885F95 - productType - com.apple.product-type.library.static - - 9F6CADB5FA31303810DB4D0B5B4DD4FB - - fileRef - 091C6AC479BB1B465BF293191BA4EB90 - isa - PBXBuildFile - - 9FEE2BAE7A1351C991273FC66949E3F1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - AFNetworking.xcconfig - sourceTree - <group> - - A0012A686D96FC5333B2F6A5BF1E9934 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - AFURLSessionManager.h - path - AFNetworking/AFURLSessionManager.h - sourceTree - <group> - - A0AD231A11A54F59BC8D486946D2928B - - fileRef - 56A0276D3AB246AD2C65A0BCABD3B4DA - isa - PBXBuildFile - - A127139E6D344D33780C1F44A44E59CA - - fileRef - 52779F8EC04ABF3285125604A009221B - isa - PBXBuildFile - - A12D5C435F39CD32EC30EA1963C64652 - - includeInIndex - 1 - isa - PBXFileReference - name - POPGeometry.mm - path - pop/POPGeometry.mm - sourceTree - <group> - - A174822E2D67CE8E0448F1C5D1FAC183 - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - B4A3E08D49931371F7247521A50EAF79 - remoteInfo - NJKWebViewProgress - - A20A8D5F5488C64E37B6B88FD84647BD - - baseConfigurationReference - 2C57CC0E4887851E2CBD72D0C00A7BAD - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/DACircularProgress/DACircularProgress-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - A2982E2A64283154B31B5E7106754649 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - AFImageDownloader.h - path - UIKit+AFNetworking/AFImageDownloader.h - sourceTree - <group> - - A2A3685A707B7F40BBDE1A6E0C7FE7DD - - fileRef - DF3432DD7E06021EE8A3D9E83F920074 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - A350C5704A1F233DAD830E081E00F8C1 - - fileRef - CA2C0447445BCAC7239897F846069EFB - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - A36CCBEBB32BAD01DBB62096F7320BAC - - children - - 41879D4AAE5C462EB8817412392E73D7 - 7C1D52E39E6EFCD821AC6ED8D9F7FF80 - 74E3F11060C970BF46AC486E730E76C8 - 72A46D3074C7025978DFFC986BAA8558 - 34D133AE0C68848DDAEAD044A0B123BE - E277947F0FB6A49FACD396F0572E2D37 - - isa - PBXGroup - name - DKNightVersion - path - DKNightVersion - sourceTree - <group> - - A4A8D730C2FC0903260A37A8B2140C40 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - NSObject+DeallocBlock.h - path - DKNightVersion/DeallocBlockExecutor/NSObject+DeallocBlock.h - sourceTree - <group> - - A4C51863329640FB465DC94DCD400F8B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UINavigationBar+Animation.m - path - DKNightVersion/Manual/UINavigationBar+Animation.m - sourceTree - <group> - - A547A69F558C5AF4063116B5107E2291 - - includeInIndex - 1 - isa - PBXFileReference - name - POPLayerExtras.mm - path - pop/POPLayerExtras.mm - sourceTree - <group> - - A60A4B3054017180C0B6C7B24E651E0A - - fileRef - C31541DAA81F30A9B41D8D1FBFAB64B9 - isa - PBXBuildFile - - A62DFBB56609C41B67DA82DB9ABB534F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIScrollView+MJRefresh.m - path - MJRefresh/UIScrollView+MJRefresh.m - sourceTree - <group> - - A663DDF3607645CBEEEF2468FD2F4395 - - buildActionMask - 2147483647 - files - - 311CDAAE66F1AB4FF57802B6E4B45468 - 9F6CADB5FA31303810DB4D0B5B4DD4FB - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - A6993E0B001E6EB572176DFCF9B87C6A - - buildActionMask - 2147483647 - files - - 5F4AECEF356E35995112F9167B219AD6 - 82AC9861A9FE0BEFEC04D31C3B05ABB2 - EF9006327AFFDBED3FD589DA04E2D36A - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - A6D80A2219B8458E2D2DA26EEF530CA2 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - metamacros.h - path - DKNightVersion/extobjc/metamacros.h - sourceTree - <group> - - A6DD5BD593CF7F708355BD477814A8AC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - NSString+MJExtension.h - path - MJExtension/NSString+MJExtension.h - sourceTree - <group> - - A70CDAD61F90AC503C7D04CC22DA2923 - - buildSettings - - ALWAYS_SEARCH_USER_PATHS - NO - CLANG_CXX_LANGUAGE_STANDARD - gnu++0x - CLANG_CXX_LIBRARY - libc++ - CLANG_ENABLE_MODULES - YES - CLANG_ENABLE_OBJC_ARC - YES - CLANG_WARN_BOOL_CONVERSION - YES - CLANG_WARN_CONSTANT_CONVERSION - YES - CLANG_WARN_DIRECT_OBJC_ISA_USAGE - YES - CLANG_WARN_EMPTY_BODY - YES - CLANG_WARN_ENUM_CONVERSION - YES - CLANG_WARN_INT_CONVERSION - YES - CLANG_WARN_OBJC_ROOT_CLASS - YES - CLANG_WARN_UNREACHABLE_CODE - YES - CLANG_WARN__DUPLICATE_METHOD_MATCH - YES - COPY_PHASE_STRIP - NO - GCC_C_LANGUAGE_STANDARD - gnu99 - GCC_DYNAMIC_NO_PIC - NO - GCC_OPTIMIZATION_LEVEL - 0 - GCC_PREPROCESSOR_DEFINITIONS - - DEBUG=1 - $(inherited) - - GCC_SYMBOLS_PRIVATE_EXTERN - NO - GCC_WARN_64_TO_32_BIT_CONVERSION - YES - GCC_WARN_ABOUT_RETURN_TYPE - YES - GCC_WARN_UNDECLARED_SELECTOR - YES - GCC_WARN_UNINITIALIZED_AUTOS - YES - GCC_WARN_UNUSED_FUNCTION - YES - GCC_WARN_UNUSED_VARIABLE - YES - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - ONLY_ACTIVE_ARCH - YES - STRIP_INSTALLED_PRODUCT - NO - SYMROOT - ${SRCROOT}/../build - - isa - XCBuildConfiguration - name - Debug - - A895E55F6B119E7127FC0AE7B0EBEC70 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libpop.a - path - libpop.a - sourceTree - BUILT_PRODUCTS_DIR - - A89B33DBDA4E99005E1F1BDB95A296CC - - fileRef - 18C516E3C7EF5B8FE88EFF08CBD156DE - isa - PBXBuildFile - - A8FC6A53F6C1CB4DEFD972664E1AF3B1 - - fileRef - CBF9380BB5C2DDF48C95A31A58DCFAE5 - isa - PBXBuildFile - - AA58D3EEFCBED00AA256325D80519762 - - fileRef - 56D8735A9A14A1E09220E42B41D34792 - isa - PBXBuildFile - - AA825D3414D0B6F349D814369D29E3CC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - NSObject+MJClass.m - path - MJExtension/NSObject+MJClass.m - sourceTree - <group> - - AB60DE99B9805BCAABF0A973CA318225 - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - 9775596B3CA16905FC8F66DA8E2A3C67 - remoteInfo - DACircularProgress - - AB863E2FD26779958F545A74EDD20269 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIScrollView+MJExtension.m - path - MJRefresh/UIScrollView+MJExtension.m - sourceTree - <group> - - ABE3A16EB1E60D35496FEB3F0A151CF4 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshComponent.h - path - MJRefresh/Base/MJRefreshComponent.h - sourceTree - <group> - - ABE7CAE4AAC89EE157FD4B84E8227453 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshAutoNormalFooter.h - path - MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h - sourceTree - <group> - - AC5590B6E86B02889D07FB7A3716BD30 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - Pods-TTNews-dummy.m - sourceTree - <group> - - ACBD8DCB5A83BEC4D347964EE483633A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshFooter.h - path - MJRefresh/Base/MJRefreshFooter.h - sourceTree - <group> - - ACE9C1A6E8D5121C6C5F907C7A9CFCC9 - - fileRef - 0B18F2E823D3FC778625EC66708088FB - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - AD09B0A836E6685EBF50393BAB033DF7 - - children - - 851D64F16B178959E32F16A169FDFCB6 - 2DB64823647E73A39B63E40A7480CF08 - 60C1CF7C667D118176C1D53B6F8D1EE9 - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/pop - sourceTree - <group> - - AD359A96DD4AA2C962E50A5CD5524F54 - - fileRef - 82C22E73E0E73736F0D81DF58D56BF2A - isa - PBXBuildFile - - AD52304275AA73231A0233CE124CA192 - - fileRef - 4F42E71491331FB254473FBD707274A3 - isa - PBXBuildFile - - AE8D8205DAE7034FBBAA118119C1BA8A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - FMDatabaseQueue.h - path - src/fmdb/FMDatabaseQueue.h - sourceTree - <group> - - AEDD7A8D8D423118CC2B1C97D6026850 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libSDWebImage.a - path - libSDWebImage.a - sourceTree - BUILT_PRODUCTS_DIR - - AF0A8BAD9494C38755C481AB2E872E40 - - fileRef - F3232BADDB3299DCBB542A6B51C35B10 - isa - PBXBuildFile - - AF20DCCA2D55DC0A9EF54380B1FAB4FF - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshComponent.m - path - MJRefresh/Base/MJRefreshComponent.m - sourceTree - <group> - - AF37E37112957F46A9484808F7E36DC5 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SVRadialGradientLayer.m - path - SVProgressHUD/SVRadialGradientLayer.m - sourceTree - <group> - - B02DFDD190C266C78D05B818034EF68B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIBarButtonItem+Night.h - path - DKNightVersion/UIKit/UIBarButtonItem+Night.h - sourceTree - <group> - - B052DBB73F6AC43F438CA23B4F1E454C - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - AFHTTPSessionManager.m - path - AFNetworking/AFHTTPSessionManager.m - sourceTree - <group> - - B07BBB97A8C5D07161059D3E56079481 - - fileRef - ED36DFF964BE5B65765A3A76180A167D - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - B0BE398350F83A7B0102C3BBAFC51428 - - fileRef - 7A4FC2348D43AB90D1E85D6D949862F6 - isa - PBXBuildFile - - B11CA320B46247088085A3E410AC08D9 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIToolbar+Night.h - path - DKNightVersion/UIKit/UIToolbar+Night.h - sourceTree - <group> - - B19CA8B0FD349AEAD2D5A1C6B46AF47D - - fileRef - 61E82DD9438246B84050F9C5D7C5DFD6 - isa - PBXBuildFile - - B1BAAA38911400E648E59090A53A3A1D - - children - - B64AF01A3EBF58B66054F121B692E438 - 7E20A38F3E9ADB2BC380D038FACD345E - A36CCBEBB32BAD01DBB62096F7320BAC - 77E1C31349DC16065E3C5C6DB868336D - CBB01F55F0192AB90EB581612A26DB54 - ED67AD1BE70DB7C223678DA99A0C2750 - 58B71F76E619D1EDE3BA65D4540FD9D2 - 24F963DE832970C9AC8056FD46B23945 - 1D0CF8793970F85343EE5875F7A8048B - 39B2562FBF3E995AB7EF8EC0A2DDDC34 - - isa - PBXGroup - name - Pods - sourceTree - <group> - - B2CFF926C9DD83E0B77850858B23C6D5 - - buildActionMask - 2147483647 - files - - 1F3C80014CF625A855DA90256013B7BF - 766D31A079D0044C43AE2B4D9F4E0DBE - 86903773F3A1C526B318640363013935 - 198E7D67D49F0C515CB8F4572BF27327 - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - B36EEF4BF268A46C305BD7A34F373D0C - - fileRef - 2DB64823647E73A39B63E40A7480CF08 - isa - PBXBuildFile - - B3A9C89E1187A533CB4FF22471C7A0E3 - - children - - 01192DA27188D8A96EDAD3BD64D772E0 - 94DF3A6264A6FDA5D4C1C563D39B3267 - A4A8D730C2FC0903260A37A8B2140C40 - 755762CB5409A3179B707E034F5B138B - - isa - PBXGroup - name - DeallocBlockExecutor - sourceTree - <group> - - B3C5355D4EB1283772BBC7536DC108DA - - children - - 1D2A4D35E62D015750C7EBA7FED4C2F9 - 499C9F2CD935BFA28C90DB4923003D55 - FA2B1336B2BF6DD744322AE19226286F - 8D4BBA5A842DBAD4BC1AA0832751C3C4 - - isa - PBXGroup - name - Serialization - sourceTree - <group> - - B418C9BDCF8C6BAC2E3942F157BB6D4C - - fileRef - EE985FC4FFB7CB8D5CAB3C64C18AA2E3 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - B4A3E08D49931371F7247521A50EAF79 - - buildConfigurationList - D5BA31DD0B8A4F55B5014D132B6BFFF5 - buildPhases - - 3619A66CEAF955C359CF25CBCC468ADE - 9059AC276AEAEE67CA6CD37776499A2F - EA5686F09F8A6A619FEDFE0C36083294 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - NJKWebViewProgress - productName - NJKWebViewProgress - productReference - 3C07EBBB796B6A49E82ADDF080D203E1 - productType - com.apple.product-type.library.static - - B51AA1F7701DCDBCC9B88A15320F9C2C - - fileRef - FA2B1336B2BF6DD744322AE19226286F - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - B56BC1B5AF7240A3B2E50EC7A163A638 - - buildConfigurationList - 0A3B4632BDEF188FCC6093F85498FABD - buildPhases - - 3EDADE731538CC87185FE9D8564CF17D - D81991763D6FBA06FB0D062199DDA572 - B2CFF926C9DD83E0B77850858B23C6D5 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - SVProgressHUD - productName - SVProgressHUD - productReference - CD67C244234E345DF392971565F69F21 - productType - com.apple.product-type.library.static - - B5A8A60810B99D763E3A3F076F63509D - - fileRef - 6AFF341D5C4726BF24BDDB6C1446C591 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - B5CF67F7870BAB70A76E57479B706B42 - - fileRef - 5D457827BFE445AF613A559D457750CE - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - B64AF01A3EBF58B66054F121B692E438 - - children - - BCE913E0DA0046134F707A592D2E858C - DDCCBF62894118E3AD48BDAB26E3F870 - B990E83466B12C856A56880B48C5C1D1 - E76F894BF753ECFA6DF91AC09956C6AC - B3C5355D4EB1283772BBC7536DC108DA - F9A3D8B1752FEE7D785DFBC6A870F4B0 - 9655D06F88A9A5D7E1F2640BBD32DBC1 - - isa - PBXGroup - name - AFNetworking - path - AFNetworking - sourceTree - <group> - - B69CC157C2F527E095D10CB5AA292E49 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SDWebImageManager.h - path - SDWebImage/SDWebImageManager.h - sourceTree - <group> - - B7231BC89D554460695EB1AFB6CD47A3 - - fileRef - E16024AC613F715D2C527B5D62649D6D - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - B736674E245CEF6684053B0233339ED7 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPPropertyAnimation.h - path - pop/POPPropertyAnimation.h - sourceTree - <group> - - B834D7AC4A775B5387745E9EDD8C2CE6 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libMJExtension.a - path - libMJExtension.a - sourceTree - BUILT_PRODUCTS_DIR - - B8A4F5CBA9FF98AD3C0B5595DAE56278 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - FMResultSet.h - path - src/fmdb/FMResultSet.h - sourceTree - <group> - - B9155B26E8C2310F99E505C74CE287A0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIImage+MultiFormat.m - path - SDWebImage/UIImage+MultiFormat.m - sourceTree - <group> - - B981229135DBE9773A86BDB116C368D1 - - buildActionMask - 2147483647 - files - - 19E6319F6ECED7539AABBD9A211F7AEC - F3774B1449FCE5B3CA3E1B336E11D284 - 326A543AD4E39A2DAECA1CD385E3AEA2 - 3939531EB270B4145C7ADFBAB2E5695F - 0A08FC71C4E3B8E0F8734BCDA7CCA7CB - ED69D9B5A342FE9BD3F170D5CDDEC948 - DFDAC8245626953C4BCE5A2DA6BE2A5B - 2359387620C5E815BF0594BC40311734 - B51AA1F7701DCDBCC9B88A15320F9C2C - 3511AE7ADBB71D05565854F0E70B7D08 - 830050886B18F54E17F117FD8EFC717B - E212C2AA95E8F6EFB69A5F1455F291F8 - C1B65C3D849FEE4FB5298A8135A765DA - 6EC97BDB3FC508526F9E388095A7DDCC - 85FAB6F1D2FE0598BF2E61D7BB084B8D - 5D52DB6DF2E6C24CECB0015A9177A2E1 - ACE9C1A6E8D5121C6C5F907C7A9CFCC9 - B5CF67F7870BAB70A76E57479B706B42 - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - B99088FC0CED9CF0C12C78E9A7FD5965 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimationEvent.h - path - pop/POPAnimationEvent.h - sourceTree - <group> - - B990E83466B12C856A56880B48C5C1D1 - - children - - 11DDA56DBF5BB9C523903E0BC8C95B93 - 0B7448F84E1B4C725EE8C8A30AE4C1BF - - isa - PBXGroup - name - Reachability - sourceTree - <group> - - B9935BDB8DB6AB67BC4DD470495C3C28 - - fileRef - 7EA9E907BE2F9926B8D7328C89B8F884 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - B9B44F1EDC36C095666CA0AE98371C09 - - fileRef - 277123334AA32A5F4F47DB30CADF4A6A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - B9C36A50069E553B4B8538E291864BB5 - - fileRef - CF9F0F78B7244A21513FFBF1DA130CDB - isa - PBXBuildFile - - B9F6903B4A43498CC8B11B1D2B6253A3 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - TransformationMatrix.h - path - pop/WebCore/TransformationMatrix.h - sourceTree - <group> - - BA62DF0497C88BDDFB7973546198E641 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - SDWebImage-prefix.pch - sourceTree - <group> - - BA6428E9F66FD5A23C0A2E06ED26CD2F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text - name - Podfile - path - ../Podfile - sourceTree - SOURCE_ROOT - xcLanguageSpecificationIdentifier - xcode.lang.ruby - - BB5F7E48CF4B7CB1DE4B01F6CC5B876A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshBackGifFooter.h - path - MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h - sourceTree - <group> - - BCA89B9A0551A9AB6D26A089A419C6ED - - fileRef - 241AFDF1A45B8F607AB3201F5B4B8D9D - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - BCE913E0DA0046134F707A592D2E858C - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - AFNetworking.h - path - AFNetworking/AFNetworking.h - sourceTree - <group> - - BD4C5BE0A37EB2CD33DF2BE21B98B9B9 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - NSData+ImageContentType.m - path - SDWebImage/NSData+ImageContentType.m - sourceTree - <group> - - BD62C61C5E4BCE2BDC8B9AACCDF39161 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UISwitch+Night.h - path - DKNightVersion/UIKit/UISwitch+Night.h - sourceTree - <group> - - BE84F592718A7860CA775F488A519E13 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPGeometry.h - path - pop/POPGeometry.h - sourceTree - <group> - - BF30D702D51DD1ED6EA45DFDB6E99923 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libPods-TTNews.a - path - libPods-TTNews.a - sourceTree - BUILT_PRODUCTS_DIR - - BF780D48A3F3F6BB7106595FF27324B1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UITextField+Keyboard.h - path - DKNightVersion/Manual/UITextField+Keyboard.h - sourceTree - <group> - - C0EB2BD985AFD7BF45BACFD7BA78D0DB - - fileRef - CD02648297C3D220A821B3894D170720 - isa - PBXBuildFile - - C16442D25F534C602780E6FD44C6A698 - - fileRef - 539FE091D2639896C01F77D746C8F553 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - C1B65C3D849FEE4FB5298A8135A765DA - - fileRef - 2EB1364F6A10CE22A5B3DE8BB3DB5F74 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - C1DAA515EEE44666FC7C3CCAB35036F9 - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - C2201E34603B014192F0967473518496 - - fileRef - 8D4BBA5A842DBAD4BC1AA0832751C3C4 - isa - PBXBuildFile - - C24BCF3D9DFDCEDB6A0DD4F3C7592508 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SDWebImageDownloaderOperation.m - path - SDWebImage/SDWebImageDownloaderOperation.m - sourceTree - <group> - - C28B9C6A32E20244B57A39EAF5D1DAF4 - - buildActionMask - 2147483647 - files - - 21B5CEBD9CFE81A0C1CEC5A61477671C - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - C2C0D8D1C74ADF26572BE769258B867F - - children - - 35254A7C812809C0E8FEB552C28D9300 - 51CD928E3D0955D5E7CD4A2D5D57C060 - - isa - PBXGroup - name - Core - sourceTree - <group> - - C31541DAA81F30A9B41D8D1FBFAB64B9 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJPropertyKey.m - path - MJExtension/MJPropertyKey.m - sourceTree - <group> - - C37D2F9A62232C28DAF2E51CF1364F21 - - fileRef - CC1DB65841558EDAA0FA1A74B1F772D6 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - C39A747A22EF6ADB1D6E1991564E209C - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIImageView+WebCache.h - path - SDWebImage/UIImageView+WebCache.h - sourceTree - <group> - - C3D988A1C04C58B05B6982CB03E6187C - - fileRef - F9AE7BB5E1C98EB11CF8A4A95A23BC11 - isa - PBXBuildFile - - C3FB3AB6EB669A20014B966F1A3AA94F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SVProgressAnimatedView.m - path - SVProgressHUD/SVProgressAnimatedView.m - sourceTree - <group> - - C47D45480990F48724C399B819C8BEF8 - - baseConfigurationReference - DC5AA875B416F9168CCBE529560EDE1F - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/SDWebImage/SDWebImage-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - C4A995180B7B634412B6E3A4AB227BC7 - - fileRef - 863E932B2A1BFCA7D0C139DAEC6E2141 - isa - PBXBuildFile - - C4B2749768B6C5B49E954FC0B47D749B - - fileRef - 7DB89CAFE4A77D0845E3A529F4C33236 - isa - PBXBuildFile - - C4F8DC5157EC439A67321F96CFF9B7A8 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIView+Night.h - path - DKNightVersion/UIKit/UIView+Night.h - sourceTree - <group> - - C51FAC8AA1BFE42FFD2ED7D6F6F7806C - - fileRef - 93313A2703EA8405C961C7A4A93EBA00 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - C56AECD3FC3A9CAAC17D5CB170571FAF - - fileRef - 7D0852C548A592E2898D99287F864873 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - C66A994F14BA080776BB3E2AD8CC55B4 - - fileRef - 8250CD8A70A80EBC4B93B705D50126B2 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - C67F32FF951D1979EF0E6B4ADED5E8A8 - - fileRef - 1BFEBB99F8B6C29BC3E0ED76F43BF9A6 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - C74208C0F7AE5568BAE95BB70E4889BC - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimationEventInternal.h - path - pop/POPAnimationEventInternal.h - sourceTree - <group> - - C8924793F687FCA9978BA8B646B9F11E - - fileRef - 5C217FF8A1665FCC43504CF1EF23C2F5 - isa - PBXBuildFile - - C8FE1703C2FE4E4FCE270AB64D41C43B - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SDWebImageDownloader.m - path - SDWebImage/SDWebImageDownloader.m - sourceTree - <group> - - C917B030D2D63B74FD5D309F008FAF8A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshConst.m - path - MJRefresh/MJRefreshConst.m - sourceTree - <group> - - CA027706DD6805766F1F4EC548E72440 - - fileRef - 1C8F38CF850A15721D65BF688AB350C2 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - CA1EB1DB5C89E63A19ED05C55E35AF23 - - fileRef - 8C641D918A23FE580FE044435953E8E8 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - CA2C0447445BCAC7239897F846069EFB - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - FMDatabaseAdditions.m - path - src/fmdb/FMDatabaseAdditions.m - sourceTree - <group> - - CAAB576A1AC2C18502E55A7CCED246BD - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJExtensionConst.m - path - MJExtension/MJExtensionConst.m - sourceTree - <group> - - CAE25E4E869D5542DECB75F475E39160 - - fileRef - 3CA795C0836DC5D571CC4D6853CEEAFC - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - CB381A5DB992CC381EEEA9386162CB3C - - baseConfigurationReference - 851D64F16B178959E32F16A169FDFCB6 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/pop/pop-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - CBB01F55F0192AB90EB581612A26DB54 - - children - - CC1DB65841558EDAA0FA1A74B1F772D6 - 4DB59CEFDCAB10EE888132C34E5A49AF - CAAB576A1AC2C18502E55A7CCED246BD - 15DD053288012DFB4833EF207C32D62A - 474A8123146C16382C7A0C078E0EDBB0 - EAEC739FBB8097975ADCBF18650341E3 - 56A0276D3AB246AD2C65A0BCABD3B4DA - F028843238E0416459CDD5317A21B779 - C31541DAA81F30A9B41D8D1FBFAB64B9 - 0A6A3B2BDB42B167BC55FE07F4D601BC - 9AEAC543BAB904679252AB4156F793DA - 8C641D918A23FE580FE044435953E8E8 - AA825D3414D0B6F349D814369D29E3CC - 09C8E3712D8323A3E21C99206F86CCDF - 881AE394748186FC66252043BB5EF412 - FC88064A37B95252C5C527F0647AFA69 - CF9F0F78B7244A21513FFBF1DA130CDB - 539FE091D2639896C01F77D746C8F553 - F1D1DA8A3CCA677BB604226A1D1967A3 - A6DD5BD593CF7F708355BD477814A8AC - 3D0901C2A4AB851402CDA86A62506F55 - FD40D7530CEE4C5F266B745DBD70E027 - - isa - PBXGroup - name - MJExtension - path - MJExtension - sourceTree - <group> - - CBF9380BB5C2DDF48C95A31A58DCFAE5 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - AFNetworking-dummy.m - sourceTree - <group> - - CC180EE0FCC1779DBDAEB2D74C4233A3 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimationInternal.h - path - pop/POPAnimationInternal.h - sourceTree - <group> - - CC1DB65841558EDAA0FA1A74B1F772D6 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJExtension.h - path - MJExtension/MJExtension.h - sourceTree - <group> - - CC58C2530FCA0ADE31B5081F6868EB71 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshAutoGifFooter.h - path - MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h - sourceTree - <group> - - CD02648297C3D220A821B3894D170720 - - includeInIndex - 1 - isa - PBXFileReference - name - POPPropertyAnimation.mm - path - pop/POPPropertyAnimation.mm - sourceTree - <group> - - CD1ADC471EE4AA1560ED050C3D73EF73 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - FloatConversion.h - path - pop/WebCore/FloatConversion.h - sourceTree - <group> - - CD24ED5388A065375C70FECF0160F0CF - - fileRef - CEC9003853B9FD98D0B888BA4B3D53E5 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - CD4CF5F1389281BCE40DF30AD1945D03 - - fileRef - F1D1DA8A3CCA677BB604226A1D1967A3 - isa - PBXBuildFile - - CD67C244234E345DF392971565F69F21 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libSVProgressHUD.a - path - libSVProgressHUD.a - sourceTree - BUILT_PRODUCTS_DIR - - CDA4DB994BAC351F4CD71EC283055E57 - - fileRef - 881AE394748186FC66252043BB5EF412 - isa - PBXBuildFile - - CDDC830C532232DE952AE10D191FADE5 - - fileRef - 3912F979B38D666625239EFC7A5198E0 - isa - PBXBuildFile - - CE7481975398572ACE09292B7E3FC657 - - fileRef - 420E44B31095ED6C5B4CCF417C37EC23 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - CE8965366A3B07A6C0615055A31B8B83 - - fileRef - 1A5C4AB96ED7AE020BE2FBABF668A88E - isa - PBXBuildFile - - CEC9003853B9FD98D0B888BA4B3D53E5 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SDWebImageDownloader.h - path - SDWebImage/SDWebImageDownloader.h - sourceTree - <group> - - CEF60CA8D3A399EF2E4112DED3182BF6 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJRefreshAutoFooter.h - path - MJRefresh/Base/MJRefreshAutoFooter.h - sourceTree - <group> - - CF9F0F78B7244A21513FFBF1DA130CDB - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - NSObject+MJKeyValue.m - path - MJExtension/NSObject+MJKeyValue.m - sourceTree - <group> - - CFEC0EB589B90099B25BF510F4AB93C0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - FMDatabasePool.m - path - src/fmdb/FMDatabasePool.m - sourceTree - <group> - - D054768D94E678B8A0D7F2AB4FF45E54 - - fileRef - 2A60A16603958DEEDDE0C61B685BA98F - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D07FFE9E17E9D36E5975493AE2B42A47 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPDefines.h - path - pop/POPDefines.h - sourceTree - <group> - - D0C5FF5485EAA2CDA03E9796B407697A - - fileRef - AE8D8205DAE7034FBBAA118119C1BA8A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D0D5FFE53407F16C9CD3D0ECF43ACE93 - - fileRef - 272D2CAB1752F0D1722D5C966DF7DF4F - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D1D1F11808DACCA88C55636F85A58FD8 - - fileRef - 3D0901C2A4AB851402CDA86A62506F55 - isa - PBXBuildFile - - D1FFF23830CEBBF7B04A425E757F39AF - - buildActionMask - 2147483647 - files - - 704F52D98420017798AE29B7E9E576D3 - 51338B86242ECA265D4D1E29779CE5B7 - 384DC68188D48D502A3D96C78153EA2F - 0C54C5B996478DAE7DBCED96888191A5 - C56AECD3FC3A9CAAC17D5CB170571FAF - 790FC674679ACC231DED3229818F4B7D - 324E6E6D50AF00797862F43C84370C95 - FEC736E348F68613F460A243DB5BC6F8 - B7231BC89D554460695EB1AFB6CD47A3 - 0694A1D2B7E243582036E55804D87ACF - 28CBA4F785D01A442C72A29EB6997242 - 981C847131F1E694A293E133DDE918DE - 625911E7714259492D59783AAF218FD7 - 78BC37B580ACA8D2F846A64B667E5A46 - 9B6303B955BA5B60BB4221B05D1609CF - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - D20789BF1878F20DE16A73D53018DBC7 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - MJExtension-prefix.pch - sourceTree - <group> - - D20C3F518C8BEEF7CA31DF91F577FD73 - - children - - 3751D4A2116620F86DA90FCB7C8D82FF - D451BC77C4A67866A807AFD37958C517 - 028E80C92E6914068BF2355A079D3E9B - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/FMDB - sourceTree - <group> - - D2259CB60A2F9266A9285E57A74AC735 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - SVProgressHUD-dummy.m - sourceTree - <group> - - D30B6A697C4F0DD87A2A674D2C470190 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIButton+Night.m - path - DKNightVersion/Manual/UIButton+Night.m - sourceTree - <group> - - D316AA0D2FBBFF2EBA6F1E8B7030E86F - - fileRef - FC393A9B89491596F3BF5B6F7702B70A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D31FEE51AF4BE2A6CC059F80B983CD3B - - fileRef - 75A8FFD8DE5318E898CB3C5D6F0D80F2 - isa - PBXBuildFile - - D3F22CED8E9BADDDE5C1B3EA1721D809 - - fileRef - D07FFE9E17E9D36E5975493AE2B42A47 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D41D8CD98F00B204E9800998ECF8427E - - attributes - - LastSwiftUpdateCheck - 0700 - LastUpgradeCheck - 0700 - - buildConfigurationList - 2D8E8EC45A3A1A1D94AE762CB5028504 - compatibilityVersion - Xcode 3.2 - developmentRegion - English - hasScannedForEncodings - 0 - isa - PBXProject - knownRegions - - en - - mainGroup - 7DB346D0F39D3F0E887471402A8071AB - productRefGroup - 930323DB8B63DAFC99CD413F47DC01A1 - projectDirPath - - projectReferences - - projectRoot - - targets - - 928353533005A4198EBDA5B700D37B64 - 9775596B3CA16905FC8F66DA8E2A3C67 - E96531D2698B334C4DEC89A22248E32D - 9F12FB4A63E20F601CFB2E64A65B57C1 - 9EAB3E42EF957541E6C3C76D35FD008E - 8F74D9EA91F4C43190670066BAC37D44 - B4A3E08D49931371F7247521A50EAF79 - 2F639BAC2BE0057D2AF305181E4637DE - 53406FD23904169597858D7716FC7645 - 63491E1EA0E903734AFD83F4D7DC319A - B56BC1B5AF7240A3B2E50EC7A163A638 - - - D42AB85B0E0CC63FA67D04E1842BB85D - - fileRef - 8E6FEA50B8951C38D0598B79D0DDD30D - isa - PBXBuildFile - - D451BC77C4A67866A807AFD37958C517 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - FMDB-dummy.m - sourceTree - <group> - - D487D299A358CFFDB06F1A7782E02E0F - - buildConfigurations - - CB381A5DB992CC381EEEA9386162CB3C - FC070B75550B92CA74429943CFDD4223 - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - D48BAC96BAD2B109BEFC35BCEFF69FFD - - fileRef - B11CA320B46247088085A3E410AC08D9 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D4986E1E3F01B19CB9A150F0692DC205 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshBackNormalFooter.m - path - MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m - sourceTree - <group> - - D4EBADFA6A60CCED11FDD6595C3770B0 - - baseConfigurationReference - 2C57CC0E4887851E2CBD72D0C00A7BAD - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/DACircularProgress/DACircularProgress-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - D53A2246773AC2B14A181B7A01EF8BBD - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SVIndefiniteAnimatedView.m - path - SVProgressHUD/SVIndefiniteAnimatedView.m - sourceTree - <group> - - D564ACA234730FF58D278BB6242FDEE4 - - fileRef - 755762CB5409A3179B707E034F5B138B - isa - PBXBuildFile - - D595C17C62C58E80EEC6F9EBCCF4050A - - fileRef - 7C1F8789E40EFE0223E50570FC68930E - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D5BA31DD0B8A4F55B5014D132B6BFFF5 - - buildConfigurations - - F05C1E1BBD544049DE9005A5602FAB90 - 898AAE5FD2A76A7B287B4525A98119DF - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - D5CA8819E10405553F9282338F19E0B3 - - fileRef - A12D5C435F39CD32EC30EA1963C64652 - isa - PBXBuildFile - - D60DAB6D6944DED56527459DD636BA39 - - buildActionMask - 2147483647 - files - - 21889668396C59E8282A365BF440A23A - 85F7BDA2650A545C7BD53DEBCEAEB714 - 8321302C0702383F917BADA569BC2B6A - 0B6CAFF0BAA701C00CBDE7665A050999 - CDDC830C532232DE952AE10D191FADE5 - DE8DFE7121E5C772A7A9B7F3D45324C8 - 96F9E868F50D2514B84278D088A919D7 - D564ACA234730FF58D278BB6242FDEE4 - C4A995180B7B634412B6E3A4AB227BC7 - 1F6CBEC31D8A4E25692573B7CF63E7A1 - 4BB3AD61E5E3DB2132D62C60DE2CF52D - DD6C62122658E5D9F95BB8B7C33BA090 - 7D1A75A73B29D589B3596C8C127AB4BD - 17896FF148C391D06F24C4109867E0B9 - 54CE2C4624F292017FD23CDB86BA3FAD - E90EB2E4A3F31422B52182BB2F6DD948 - FB16E07D610A3D0F3931B4648E954827 - AF0A8BAD9494C38755C481AB2E872E40 - 5E8E570F4C8B8B59F1FE6BC21DF7DB3F - 8CBAC87BB0370982F1EA12173D15FFB4 - C3D988A1C04C58B05B6982CB03E6187C - FCF8D8DFB7593DBADB82D5D666D64D45 - 2F74B001E6DE91044F623C89F21EEB18 - 07FC64DEA7A7CD353B15D6EFC9499733 - 760C261E5F5A81992095283D02FF410B - 14E98BAF93AF8625043D2CF46DCF93CD - 55EAD85D305CED6AB67C325F4FEE5FA6 - AD52304275AA73231A0233CE124CA192 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - D68361F633344D64600A8098AD885F95 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libFMDB.a - path - libFMDB.a - sourceTree - BUILT_PRODUCTS_DIR - - D6D3C74AAD773F9AC7DB44DFA9C06C25 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UITabBar+Night.m - path - DKNightVersion/UIKit/UITabBar+Night.m - sourceTree - <group> - - D6FFC7B986E04B23B6B9E98723DE63D8 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SVIndefiniteAnimatedView.h - path - SVProgressHUD/SVIndefiniteAnimatedView.h - sourceTree - <group> - - D7494EBF206671A3A2287926FAC95709 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIProgressView+AFNetworking.h - path - UIKit+AFNetworking/UIProgressView+AFNetworking.h - sourceTree - <group> - - D763D4D2575F282DFE21224FB4325CE6 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - AFSecurityPolicy.h - path - AFNetworking/AFSecurityPolicy.h - sourceTree - <group> - - D81991763D6FBA06FB0D062199DDA572 - - buildActionMask - 2147483647 - files - - 2C59234AF0BDEBC10590AA6CEF12A1DF - D9C485DF548F50BBF86CF75D1E981665 - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - D83210EEE85AF00991EE70C7B579D939 - - fileRef - B02DFDD190C266C78D05B818034EF68B - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D851FF3699E93DE46793E401B23C3636 - - fileRef - 9A378E3FA547A1DCF0276D412B8B0107 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D86281719FC4F4C2953908F433CC3CF2 - - fileRef - 02030CCD1586CDA3A48E4F7F535D9E4A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D897A669AB16723B369531F554CBCAA1 - - fileRef - 9C739E488F9426EB75D8770B832A689E - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D8C5F869FEAA514B0F07DADFB0468EC9 - - fileRef - 1E42F9417982CCC0CE97E0BA4FE3BF8A - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - D91EA5FAD7A935F7FE38A0D7167A1C50 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - FMResultSet.m - path - src/fmdb/FMResultSet.m - sourceTree - <group> - - D93E1B8D69476969600E3BE2442EC1C5 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SVProgressHUD.m - path - SVProgressHUD/SVProgressHUD.m - sourceTree - <group> - - D9C485DF548F50BBF86CF75D1E981665 - - fileRef - 358C18D56505647A3BFBAE84A7B11F5A - isa - PBXBuildFile - - D9F8E672D5CEC1D51345B9535114FC9D - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - 9EAB3E42EF957541E6C3C76D35FD008E - remoteInfo - MJExtension - - DA8BAA5F8E5949555BC8B94E309E23D8 - - fileRef - 23CB853B211A106A6145C73FB410F399 - isa - PBXBuildFile - - DAF1CE8756287B465E0DE48A6133FB79 - - fileRef - EAEC739FBB8097975ADCBF18650341E3 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - DB89E2C826F5335897375AF92A68A0A7 - - isa - PBXTargetDependency - name - SVProgressHUD - target - B56BC1B5AF7240A3B2E50EC7A163A638 - targetProxy - 065DE18CD0F7516CA7E14DE658589908 - - DBBE23FF83AA9D763461377F8C974E4A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIImage+GIF.m - path - SDWebImage/UIImage+GIF.m - sourceTree - <group> - - DC5AA875B416F9168CCBE529560EDE1F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - SDWebImage.xcconfig - sourceTree - <group> - - DCEDDEE3547FB20129BC913202642782 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UITableView+Night.m - path - DKNightVersion/UIKit/UITableView+Night.m - sourceTree - <group> - - DD1D1D97B75E32B3792D585442B300C5 - - isa - PBXTargetDependency - name - DKNightVersion - target - E96531D2698B334C4DEC89A22248E32D - targetProxy - F38D4FD0615553C92AE3715D7920B16D - - DD24BDCB6C8D8D64E71EC51553F143BA - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - path - Pods-TTNews.debug.xcconfig - sourceTree - <group> - - DD6C62122658E5D9F95BB8B7C33BA090 - - fileRef - E9F8089086DE5222A236516EDC8E9B3C - isa - PBXBuildFile - - DDCCBF62894118E3AD48BDAB26E3F870 - - children - - 713AA1BADCB8D73F03918F5A85A608D0 - B052DBB73F6AC43F438CA23B4F1E454C - A0012A686D96FC5333B2F6A5BF1E9934 - 7DE4BBEBD3DAA84CE18482CC6598F7D0 - - isa - PBXGroup - name - NSURLSession - sourceTree - <group> - - DE0BAD758C6F291CE7E10DA077987C62 - - buildActionMask - 2147483647 - files - - 14CD3DFB4A25BAEFB205730FA6750025 - 23415204AA48A02959E4E5245CF96037 - D31FEE51AF4BE2A6CC059F80B983CD3B - 4BA19F7DD8F9FDA648137B2CE89E7588 - A8FC6A53F6C1CB4DEFD972664E1AF3B1 - 43098B12DF5546CB576986E68DBCAFCA - 311E5200832E4AE174A193851A2C6317 - 8E82178CC9BD8BE3DBF2C10E33807AEB - C2201E34603B014192F0967473518496 - FCDDD3BC67EA205C87238A2A87D5C36C - 15460ABD372C937E2A07A2FCDF98C473 - 78940A6980F359C4304C695EF6E67C5D - B0BE398350F83A7B0102C3BBAFC51428 - 38EEA9ED6B922946C54AD6BAC93B73AD - 7F0578A0258E02384F6229C678E60D31 - AD359A96DD4AA2C962E50A5CD5524F54 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - DE39928A2ABC2213A0C10FA1E1CB52B3 - - baseConfigurationReference - 3751D4A2116620F86DA90FCB7C8D82FF - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/FMDB/FMDB-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - DE7CCFD559106710B274D114A9841632 - - buildActionMask - 2147483647 - files - - 8A5CF3A2E5440A7D7B011B791D01029A - DE7E3F8E8A59B3F7A77BA8951693E8E3 - 9610EB6DD09A8D2E0F53A06DF74A769C - 9CD83411F46F2567211116F44269772D - CD24ED5388A065375C70FECF0160F0CF - EB8661C73DA6828766596BE614B57F10 - 2B10B5DC0A1B4179F8200DEA4CF98984 - 9E20EB77864556B3E37D8511D3955C3E - B07BBB97A8C5D07161059D3E56079481 - DF887D63A77E999A360D04722CF70395 - D8C5F869FEAA514B0F07DADFB0468EC9 - 8653A8CA11B7C06ED3B91869DA308E95 - F71C24D6042981F76E3979935BF48684 - 38E2982FE0D63199D0352B395E280FA3 - 92177973EBA0B34751CF530522C8F73D - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - DE7E3F8E8A59B3F7A77BA8951693E8E3 - - fileRef - 3C94E0D5ED31DC71CA979DAE02211998 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - DE8DFE7121E5C772A7A9B7F3D45324C8 - - fileRef - 0C62BAD2FB539FC25C032D174B2FDA84 - isa - PBXBuildFile - - DEFA3504695E9DFDE1177DAB22884470 - - fileRef - D91EA5FAD7A935F7FE38A0D7167A1C50 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - DF3432DD7E06021EE8A3D9E83F920074 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - NJKWebViewProgressView.h - path - NJKWebViewProgress/NJKWebViewProgressView.h - sourceTree - <group> - - DF887D63A77E999A360D04722CF70395 - - fileRef - 43BA28469D72B79E4E79BFC6AE7328E9 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - DFDAC8245626953C4BCE5A2DA6BE2A5B - - fileRef - D763D4D2575F282DFE21224FB4325CE6 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - E000453534D28C45753A7F4EF281BC97 - - fileRef - A547A69F558C5AF4063116B5107E2291 - isa - PBXBuildFile - - E0529B7C623EB5752662665A05DAAFBA - - fileRef - E6506107702AEE6CBE4CE398F827369C - isa - PBXBuildFile - - E16024AC613F715D2C527B5D62649D6D - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - SDWebImagePrefetcher.m - path - SDWebImage/SDWebImagePrefetcher.m - sourceTree - <group> - - E1D3E01D8670003AC52C24C77D2FA2CF - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SDWebImageDecoder.h - path - SDWebImage/SDWebImageDecoder.h - sourceTree - <group> - - E212C2AA95E8F6EFB69A5F1455F291F8 - - fileRef - E790F4B99B3E4F056D90DE0A4A091DB1 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - E21C94827302F57629D83213E140D730 - - children - - 80865375BD7061D5F7CBB8DE19AC8233 - - isa - PBXGroup - name - Resources - sourceTree - <group> - - E277947F0FB6A49FACD396F0572E2D37 - - children - - B02DFDD190C266C78D05B818034EF68B - 3EFC21A0B4DE4EF730F826C7C7A1DC75 - 02030CCD1586CDA3A48E4F7F535D9E4A - D30B6A697C4F0DD87A2A674D2C470190 - 2036F6B0B797484514C2799195C825F4 - E9F8089086DE5222A236516EDC8E9B3C - 6E5E48F0FA2C24795C8867EB41D56EB9 - 65E2CD5CF590B2E17E78A75E17D8F309 - 81292F3844994954D696E2FB938508BE - FBB483FDC0150E805753B9E16F368216 - 82E466DC61BD55C67F8005E93FB5A125 - A4C51863329640FB465DC94DCD400F8B - 0A983F965530EC1B18645593310B35BA - 14BA402BA3FC8DEFA68ED65ED0F5AD36 - 41872B66CA6D947F4A3386450AC8F7EE - 6843FEBC1B25835C97E9E3B8558EA34B - 09CAF9E9C6F04F5C711BFC79FFAEE767 - F3232BADDB3299DCBB542A6B51C35B10 - 72645AB555ACB154314B9E0E078A93C1 - EA9CE32C3F1212B31314975B53BBD645 - 5E47F8D4425904467E8BF825C909EAB6 - 72092F11D1E8E9855A5E157C8BB11813 - BD62C61C5E4BCE2BDC8B9AACCDF39161 - F9AE7BB5E1C98EB11CF8A4A95A23BC11 - 6B34096B18606A9463DB65EF463DEAF4 - D6D3C74AAD773F9AC7DB44DFA9C06C25 - 7EA9E907BE2F9926B8D7328C89B8F884 - DCEDDEE3547FB20129BC913202642782 - BF780D48A3F3F6BB7106595FF27324B1 - 2BB14DC84BEB4B182F9995C340DE4D65 - 1962B9F0C1051AECDA8B89D66CD291FC - 42C0A6866D491E89F43CF1FA2BD0033F - 9458AE7D0741B651D9D246E907E6C0EB - 3319B4ED89F1B99A3011A4015C9C243D - B11CA320B46247088085A3E410AC08D9 - 5AE4E235669EC113377519C46334089A - C4F8DC5157EC439A67321F96CFF9B7A8 - 4F42E71491331FB254473FBD707274A3 - - isa - PBXGroup - name - UIKit - sourceTree - <group> - - E2C595890A1C1D2634DFC6C56EEC8D19 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - NSData+ImageContentType.h - path - SDWebImage/NSData+ImageContentType.h - sourceTree - <group> - - E5D09EBCCC6B815D71144CF58EDBE648 - - fileRef - 4F383980D527FBF4E1E59446EBDCABE4 - isa - PBXBuildFile - - E6506107702AEE6CBE4CE398F827369C - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshAutoNormalFooter.m - path - MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m - sourceTree - <group> - - E76F894BF753ECFA6DF91AC09956C6AC - - children - - D763D4D2575F282DFE21224FB4325CE6 - 10B7C108490F0D7DF9D60FA8088927D0 - - isa - PBXGroup - name - Security - sourceTree - <group> - - E790F4B99B3E4F056D90DE0A4A091DB1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIButton+AFNetworking.h - path - UIKit+AFNetworking/UIButton+AFNetworking.h - sourceTree - <group> - - E7A197C569F37AFB32DB951DEE7EEF5F - - fileRef - A4A8D730C2FC0903260A37A8B2140C40 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - E90EB2E4A3F31422B52182BB2F6DD948 - - fileRef - 14BA402BA3FC8DEFA68ED65ED0F5AD36 - isa - PBXBuildFile - - E96531D2698B334C4DEC89A22248E32D - - buildConfigurationList - 586A4CC1BC5368A28EADD369C4559A73 - buildPhases - - D60DAB6D6944DED56527459DD636BA39 - 0A439270E7FB7FE4B0CB65E3E6EC98D3 - 6B6E84DDC7EF101B3A8A30CDBC74FC85 - - buildRules - - dependencies - - isa - PBXNativeTarget - name - DKNightVersion - productName - DKNightVersion - productReference - 50169624A9225BE7EBCCE02910B6C40B - productType - com.apple.product-type.library.static - - E986975088E6552CE4CCEE96762A0703 - - fileRef - 51CD928E3D0955D5E7CD4A2D5D57C060 - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - E9C749C55D347270A4C1F287B1C935AF - - isa - PBXTargetDependency - name - AFNetworking - target - 928353533005A4198EBDA5B700D37B64 - targetProxy - FECE61B5327D2C3CF5CCA0DEBC98413E - - E9C94F3B1FCC268F06C8A599A810D60F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - DALabeledCircularProgressView.m - path - DACircularProgress/DALabeledCircularProgressView.m - sourceTree - <group> - - E9F8089086DE5222A236516EDC8E9B3C - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIControl+Night.m - path - DKNightVersion/UIKit/UIControl+Night.m - sourceTree - <group> - - EA03AF12E0E3E8ADC50DA70F2C5BF39E - - fileRef - 38748A5FF88F5C0A05FEED150DAC16DB - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - EA5686F09F8A6A619FEDFE0C36083294 - - buildActionMask - 2147483647 - files - - 32EFF502FBA4473781B2B65FD697B345 - A2A3685A707B7F40BBDE1A6E0C7FE7DD - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - EA938873F4EA548167C14F3C7580FC33 - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - 63491E1EA0E903734AFD83F4D7DC319A - remoteInfo - SDWebImage - - EA9CE32C3F1212B31314975B53BBD645 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UISearchBar+Night.m - path - DKNightVersion/UIKit/UISearchBar+Night.m - sourceTree - <group> - - EAEC739FBB8097975ADCBF18650341E3 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJProperty.h - path - MJExtension/MJProperty.h - sourceTree - <group> - - EB0BD4006D1A94A794ED7E72ABCF46BF - - buildConfigurations - - 3CC75902D300A05FFC9E51AC41905A0C - 6FAE6E76F74AA7218C7A5976A9A4FC9F - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - EB8661C73DA6828766596BE614B57F10 - - fileRef - 3EBB8C52D5B84F0145C7B4843E6D8196 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - EBD492D43A3A67F231799ADEBD06BBCD - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIActivityIndicatorView+AFNetworking.m - path - UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m - sourceTree - <group> - - ECB865BC44644DA90479F9C206E382D0 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIActivityIndicatorView+AFNetworking.h - path - UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h - sourceTree - <group> - - ECCC06F4F1551987F0834A094FDCB1CA - - buildConfigurations - - 3D09452A09A763F78EA561CACE303DF7 - 06BFEB1F7510B1E9C1F7A1DE02C3E17D - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - ED36DFF964BE5B65765A3A76180A167D - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - SDWebImagePrefetcher.h - path - SDWebImage/SDWebImagePrefetcher.h - sourceTree - <group> - - ED67AD1BE70DB7C223678DA99A0C2750 - - children - - 2A60A16603958DEEDDE0C61B685BA98F - CEF60CA8D3A399EF2E4112DED3182BF6 - F4053A20576AD8944AD6FD2D936A722F - CC58C2530FCA0ADE31B5081F6868EB71 - 23CB853B211A106A6145C73FB410F399 - ABE7CAE4AAC89EE157FD4B84E8227453 - E6506107702AEE6CBE4CE398F827369C - 8250CD8A70A80EBC4B93B705D50126B2 - 7707C2CA20EB25819E97181E23993FAE - 6AFF341D5C4726BF24BDDB6C1446C591 - 18C516E3C7EF5B8FE88EFF08CBD156DE - BB5F7E48CF4B7CB1DE4B01F6CC5B876A - 543D77F621A899DDD24AFC3E7F1DE129 - 3F58ED32A9E20FC74FD6793A453E5E70 - D4986E1E3F01B19CB9A150F0692DC205 - 9CB70DDC67A0424818079CAB930357FB - F789495B49A442A585248E5D25F41F73 - ABE3A16EB1E60D35496FEB3F0A151CF4 - AF20DCCA2D55DC0A9EF54380B1FAB4FF - 709FD2FD0C8CDBECB9AB43F16DC72B70 - C917B030D2D63B74FD5D309F008FAF8A - ACBD8DCB5A83BEC4D347964EE483633A - 89A36A1198F55774A94BC42AD208D3AF - 7C1F8789E40EFE0223E50570FC68930E - 826A2C5B3DDEE0964B2980EA04A5A497 - 38748A5FF88F5C0A05FEED150DAC16DB - 3989E7406CED91A29F1CB2841D65B63B - 1BFEBB99F8B6C29BC3E0ED76F43BF9A6 - 61E82DD9438246B84050F9C5D7C5DFD6 - 0CB3C59B22A9FC7BC0629E03825BE0C3 - 72D4CC2FF0C9A03F51AAA76DAB527961 - 277123334AA32A5F4F47DB30CADF4A6A - AB863E2FD26779958F545A74EDD20269 - 372BFBF6F650A94B122131510FF774E0 - A62DFBB56609C41B67DA82DB9ABB534F - 620212E8B0A175CA61A7D8B96D81731A - 4497F356CDA07B41213339D445FEDEFD - E21C94827302F57629D83213E140D730 - 0ACBCC40B07594F01081CE558F1E4979 - - isa - PBXGroup - name - MJRefresh - path - MJRefresh - sourceTree - <group> - - ED69D9B5A342FE9BD3F170D5CDDEC948 - - fileRef - 11DDA56DBF5BB9C523903E0BC8C95B93 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - EE985FC4FFB7CB8D5CAB3C64C18AA2E3 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - FMDatabaseAdditions.h - path - src/fmdb/FMDatabaseAdditions.h - sourceTree - <group> - - EF10B6328453AD89FB2858CE09CD491B - - fileRef - F45FCD3DF9B8E3EBBBF37B99BB1AC646 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - EF9006327AFFDBED3FD589DA04E2D36A - - fileRef - E9C94F3B1FCC268F06C8A599A810D60F - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - EFCDCCD66269CA908494B11E024DC736 - - fileRef - CC58C2530FCA0ADE31B5081F6868EB71 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - EFF6557ADDB96E851B58DB7774E0A797 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - CALayer+Night.m - path - DKNightVersion/CoreAnimation/CALayer+Night.m - sourceTree - <group> - - F0019A36DAA2072760CE5C16C76DF095 - - fileRef - 01192DA27188D8A96EDAD3BD64D772E0 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - F028843238E0416459CDD5317A21B779 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - MJPropertyKey.h - path - MJExtension/MJPropertyKey.h - sourceTree - <group> - - F05C1E1BBD544049DE9005A5602FAB90 - - baseConfigurationReference - 711B8F0BD15514F946EB73680D6E07E5 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/NJKWebViewProgress/NJKWebViewProgress-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - YES - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Debug - - F0D01ED940723EED434E4EF4BCFF53F9 - - fileRef - D4986E1E3F01B19CB9A150F0692DC205 - isa - PBXBuildFile - - F0DB9199DC083A489978BFDE24F78183 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - UIImageView+AFNetworking.h - path - UIKit+AFNetworking/UIImageView+AFNetworking.h - sourceTree - <group> - - F1D1DA8A3CCA677BB604226A1D1967A3 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - NSObject+MJProperty.m - path - MJExtension/NSObject+MJProperty.m - sourceTree - <group> - - F2A1B494417E8906E69CBCCC9377708F - - includeInIndex - 1 - isa - PBXFileReference - name - POPDecayAnimation.mm - path - pop/POPDecayAnimation.mm - sourceTree - <group> - - F2A9AAC03867BB4990470BAC5912E030 - - buildActionMask - 2147483647 - files - - 53FB0CBA14BE53F79F4EB4E7B46C523B - 072C184628072EDE522629576F1716DE - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - F2B999DB8AF314B4036F5CB2CA1E9657 - - baseConfigurationReference - 9446782CA90A06C472946A60B765C77C - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/SVProgressHUD/SVProgressHUD-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - F3232BADDB3299DCBB542A6B51C35B10 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIProgressView+Night.m - path - DKNightVersion/UIKit/UIProgressView+Night.m - sourceTree - <group> - - F3774B1449FCE5B3CA3E1B336E11D284 - - fileRef - 713AA1BADCB8D73F03918F5A85A608D0 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - F38D4FD0615553C92AE3715D7920B16D - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - E96531D2698B334C4DEC89A22248E32D - remoteInfo - DKNightVersion - - F4053A20576AD8944AD6FD2D936A722F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshAutoFooter.m - path - MJRefresh/Base/MJRefreshAutoFooter.m - sourceTree - <group> - - F432A374A040EBAB69C9EABC6C4D699A - - fileRef - ABE3A16EB1E60D35496FEB3F0A151CF4 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - F45FCD3DF9B8E3EBBBF37B99BB1AC646 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPAnimation.h - path - pop/POPAnimation.h - sourceTree - <group> - - F4CDA5FA9197A41E0081E84F932906EB - - children - - 4AA3B53FB26C1415A7F72CE14EE3D121 - - isa - PBXGroup - name - Frameworks - sourceTree - <group> - - F58455418F190BC37D1FD19EA3EA5B01 - - fileRef - 9CB70DDC67A0424818079CAB930357FB - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - F6AAF5593D0C5E24B3377E5C3999D0AA - - buildActionMask - 2147483647 - files - - C1DAA515EEE44666FC7C3CCAB35036F9 - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - F6B2DFB3A0EE9FD880C60B13DEBE47ED - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIButton+AFNetworking.m - path - UIKit+AFNetworking/UIButton+AFNetworking.m - sourceTree - <group> - - F7131033FF45A01C50502DB74DDC8B35 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - name - libAFNetworking.a - path - libAFNetworking.a - sourceTree - BUILT_PRODUCTS_DIR - - F71C24D6042981F76E3979935BF48684 - - fileRef - 9376D50310F2A816C6810D56C7431568 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - F765039FCC5D5506BEC765EE2B818F69 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIView+WebCacheOperation.m - path - SDWebImage/UIView+WebCacheOperation.m - sourceTree - <group> - - F789495B49A442A585248E5D25F41F73 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - MJRefreshBackStateFooter.m - path - MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m - sourceTree - <group> - - F7B852F0917A6D6540DAB54F736D681B - - fileRef - 709FD2FD0C8CDBECB9AB43F16DC72B70 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - F800B3FA3D41FC047B44DA896BDAC4FF - - includeInIndex - 1 - isa - PBXFileReference - name - POPAnimationRuntime.mm - path - pop/POPAnimationRuntime.mm - sourceTree - <group> - - F848F7F827B5A2DC7C87E97148DA88AB - - fileRef - 5BA98B517FE220F0B5FFC21BF1D6D0F1 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - F8755731C347BA806518AB8BDA3A5644 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UIButton+WebCache.m - path - SDWebImage/UIButton+WebCache.m - sourceTree - <group> - - F95AF419D96CDED026D79CDD7A3B3023 - - buildActionMask - 2147483647 - files - - C37D2F9A62232C28DAF2E51CF1364F21 - 0F7EC02E944F8719A5EBDC9D29F9E99F - 13695CE9BF36F26CA806C5CD2D6ADECA - DAF1CE8756287B465E0DE48A6133FB79 - 366A613AE07EAA35B5789F6123FB48AF - 91CAC2B69903F5F3D7C25A95CA77A600 - CA1EB1DB5C89E63A19ED05C55E35AF23 - 118227C43F283C87130BEE2BFABC7EBD - 4330562BA1A299CBD37F4602B1A4C95A - C16442D25F534C602780E6FD44C6A698 - 86D3C675657C8AFE6F73E5D0F209E990 - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - F977B243321331B58666E9284634101A - - fileRef - B8A4F5CBA9FF98AD3C0B5595DAE56278 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - F9A3D8B1752FEE7D785DFBC6A870F4B0 - - children - - 9FEE2BAE7A1351C991273FC66949E3F1 - CBF9380BB5C2DDF48C95A31A58DCFAE5 - 2B897B9F53B037B383EBFC742C5150D2 - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/AFNetworking - sourceTree - <group> - - F9AE7BB5E1C98EB11CF8A4A95A23BC11 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UISwitch+Night.m - path - DKNightVersion/UIKit/UISwitch+Night.m - sourceTree - <group> - - FA031FE32C384AF41A819924ECC0589E - - fileRef - 6B34096B18606A9463DB65EF463DEAF4 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - FA2B1336B2BF6DD744322AE19226286F - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - AFURLResponseSerialization.h - path - AFNetworking/AFURLResponseSerialization.h - sourceTree - <group> - - FADDFEF3FAD9AD71AAC4D802C69E7980 - - fileRef - 41879D4AAE5C462EB8817412392E73D7 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - FB16E07D610A3D0F3931B4648E954827 - - fileRef - 6843FEBC1B25835C97E9E3B8558EA34B - isa - PBXBuildFile - - FB45FFD90572718D82AB9092B750F0CA - - buildSettings - - ALWAYS_SEARCH_USER_PATHS - NO - CLANG_CXX_LANGUAGE_STANDARD - gnu++0x - CLANG_CXX_LIBRARY - libc++ - CLANG_ENABLE_MODULES - YES - CLANG_ENABLE_OBJC_ARC - YES - CLANG_WARN_BOOL_CONVERSION - YES - CLANG_WARN_CONSTANT_CONVERSION - YES - CLANG_WARN_DIRECT_OBJC_ISA_USAGE - YES - CLANG_WARN_EMPTY_BODY - YES - CLANG_WARN_ENUM_CONVERSION - YES - CLANG_WARN_INT_CONVERSION - YES - CLANG_WARN_OBJC_ROOT_CLASS - YES - CLANG_WARN_UNREACHABLE_CODE - YES - CLANG_WARN__DUPLICATE_METHOD_MATCH - YES - COPY_PHASE_STRIP - YES - ENABLE_NS_ASSERTIONS - NO - GCC_C_LANGUAGE_STANDARD - gnu99 - GCC_PREPROCESSOR_DEFINITIONS - - RELEASE=1 - - GCC_WARN_64_TO_32_BIT_CONVERSION - YES - GCC_WARN_ABOUT_RETURN_TYPE - YES - GCC_WARN_UNDECLARED_SELECTOR - YES - GCC_WARN_UNINITIALIZED_AUTOS - YES - GCC_WARN_UNUSED_FUNCTION - YES - GCC_WARN_UNUSED_VARIABLE - YES - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - STRIP_INSTALLED_PRODUCT - NO - SYMROOT - ${SRCROOT}/../build - VALIDATE_PRODUCT - YES - - isa - XCBuildConfiguration - name - Release - - FB46A967F3DDE62480467BEBCE75607C - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - AFNetworkActivityIndicatorManager.h - path - UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h - sourceTree - <group> - - FB8E6FDC31229DAA4D8F3B319D62E6D1 - - includeInIndex - 1 - isa - PBXFileReference - name - POPVector.mm - path - pop/POPVector.mm - sourceTree - <group> - - FBB483FDC0150E805753B9E16F368216 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - UILabel+Night.m - path - DKNightVersion/UIKit/UILabel+Night.m - sourceTree - <group> - - FC070B75550B92CA74429943CFDD4223 - - baseConfigurationReference - 851D64F16B178959E32F16A169FDFCB6 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/pop/pop-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - FC393A9B89491596F3BF5B6F7702B70A - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - CALayer+Night.h - path - DKNightVersion/CoreAnimation/CALayer+Night.h - sourceTree - <group> - - FC7245612AF6EF9AFC848BB37D4BE804 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - name - DKColor.m - path - DKNightVersion/Core/DKColor.m - sourceTree - <group> - - FC88064A37B95252C5C527F0647AFA69 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - NSObject+MJKeyValue.h - path - MJExtension/NSObject+MJKeyValue.h - sourceTree - <group> - - FCDDD3BC67EA205C87238A2A87D5C36C - - fileRef - 7DE4BBEBD3DAA84CE18482CC6598F7D0 - isa - PBXBuildFile - - FCF8D8DFB7593DBADB82D5D666D64D45 - - fileRef - D6D3C74AAD773F9AC7DB44DFA9C06C25 - isa - PBXBuildFile - - FD06763333C06C19A41242170C7CF713 - - buildActionMask - 2147483647 - files - - 9D2AA60CE2A57C9F5D79D9EF23108A5A - F848F7F827B5A2DC7C87E97148DA88AB - 1092242FE516D192B1B18C66F9622DFA - 83DC116EE7BD47DF97B31377AC71F19C - EF10B6328453AD89FB2858CE09CD491B - 956897379B9772A06F53A667C14CB13D - 9490A1BF0EAAA5CDBE60731B62DFFA85 - 9D0426E9152828FA5C8778FE46D6AB80 - 744B5A1002CD322D063CCD4A71AF2B7D - 7ED9C88EEEDA2C98867CFBABBA1945DE - 997CA834C83CB6BACC85AB9719CD28A3 - 2F800FBC10F3C6A45CB2C82759F75FD8 - 01C04156CE60363ED312F653DF579CB7 - 860400C3A21CB2E154D074B974F8420C - C51FAC8AA1BFE42FFD2ED7D6F6F7806C - 2710B519E79C54021DC7079532E108E4 - FE96BF6D08165555E1B3C1B6A92D0B25 - 68BCD15D5071D2B62D83B47D9772E23C - CAE25E4E869D5542DECB75F475E39160 - D897A669AB16723B369531F554CBCAA1 - 1163222946770435278A243AA92FCC4C - D3F22CED8E9BADDDE5C1B3EA1721D809 - 7798C71F1A8F565EF97463695B24952C - 17475EC41BFAAD98F4661BC5AE841816 - 26CB0A04F3E129D3A030F6CD6312E70D - 82364F956F965883ACC87278A945F771 - 7F15E8A2BF4A27961B1BC7CA94FC3426 - 0650713BFA545A5D8E01766A79CE43EA - 302F004632472D55C5BDEC51E19EFE66 - CE7481975398572ACE09292B7E3FC657 - 2D71E24DE096392A15B6E0B0CE98052E - 250DAA9B5622865808CB870C3D77B732 - 12EB8DEBD812483C68180606AC1B04D2 - - isa - PBXHeadersBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - FD40D7530CEE4C5F266B745DBD70E027 - - children - - 28A55EC5036EDF3C43CE79A6F884BE7A - 37BA0D6FAA1EF073108C8D0F05CC1EFA - D20789BF1878F20DE16A73D53018DBC7 - - isa - PBXGroup - name - Support Files - path - ../Target Support Files/MJExtension - sourceTree - <group> - - FDF30F97BDF167F0B956DED0F2C8DA69 - - fileRef - CAAB576A1AC2C18502E55A7CCED246BD - isa - PBXBuildFile - - FE2464AD4F60C46EDCDB721E788CE1E1 - - fileRef - 8754957EFA28F9A841D24331DFF3D3B5 - isa - PBXBuildFile - - FE96BF6D08165555E1B3C1B6A92D0B25 - - fileRef - 356C74FFF66E2F05F1D8B09ECB1CA2C2 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Project - - - - FE9B72C08EA457F2E2BF7CE320EE7BD9 - - baseConfigurationReference - 240C0A0FA89CBC560EE6CAE6C09D3F78 - buildSettings - - ENABLE_STRICT_OBJC_MSGSEND - YES - GCC_PREFIX_HEADER - Target Support Files/DKNightVersion/DKNightVersion-prefix.pch - IPHONEOS_DEPLOYMENT_TARGET - 8.0 - MTL_ENABLE_DEBUG_INFO - NO - OTHER_LDFLAGS - - OTHER_LIBTOOLFLAGS - - PRIVATE_HEADERS_FOLDER_PATH - - PRODUCT_NAME - $(TARGET_NAME) - PUBLIC_HEADERS_FOLDER_PATH - - SDKROOT - iphoneos - SKIP_INSTALL - YES - - isa - XCBuildConfiguration - name - Release - - FEC736E348F68613F460A243DB5BC6F8 - - fileRef - 8DC2468DD9EC492AC88E48D891338E9D - isa - PBXBuildFile - settings - - COMPILER_FLAGS - -DOS_OBJECT_USE_OBJC=0 - - - FECE61B5327D2C3CF5CCA0DEBC98413E - - containerPortal - D41D8CD98F00B204E9800998ECF8427E - isa - PBXContainerItemProxy - proxyType - 1 - remoteGlobalIDString - 928353533005A4198EBDA5B700D37B64 - remoteInfo - AFNetworking - - FEF1AFDFDBAEC5C7E99878879EF7A823 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - name - POPLayerExtras.h - path - pop/POPLayerExtras.h - sourceTree - <group> - - FF1FEB28260D52C222E2F87FBBC05277 - - fileRef - CEF60CA8D3A399EF2E4112DED3182BF6 - isa - PBXBuildFile - settings - - ATTRIBUTES - - Public - - - - - rootObject - D41D8CD98F00B204E9800998ECF8427E - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/AFNetworking.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/AFNetworking.xcscheme deleted file mode 100644 index e8509b0..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/AFNetworking.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/DACircularProgress.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/DACircularProgress.xcscheme deleted file mode 100644 index 48d792c..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/DACircularProgress.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/DKNightVersion.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/DKNightVersion.xcscheme deleted file mode 100644 index 93d373d..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/DKNightVersion.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/FMDB.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/FMDB.xcscheme deleted file mode 100644 index 478fed4..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/FMDB.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/MJExtension.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/MJExtension.xcscheme deleted file mode 100644 index 3cadb03..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/MJExtension.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/MJRefresh.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/MJRefresh.xcscheme deleted file mode 100644 index e66c1a7..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/MJRefresh.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/NJKWebViewProgress.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/NJKWebViewProgress.xcscheme deleted file mode 100644 index 8c53255..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/NJKWebViewProgress.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/Pods-TTNews.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/Pods-TTNews.xcscheme deleted file mode 100644 index 4eaa6d3..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/Pods-TTNews.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/SDWebImage.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/SDWebImage.xcscheme deleted file mode 100644 index 943519a..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/SDWebImage.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/SVProgressHUD.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/SVProgressHUD.xcscheme deleted file mode 100644 index b2e4697..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/SVProgressHUD.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/pop.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/pop.xcscheme deleted file mode 100644 index 992b99c..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/pop.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 8e4eba2..0000000 --- a/Pods/Pods.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,122 +0,0 @@ - - - - - SchemeUserState - - AFNetworking.xcscheme - - isShown - - - DACircularProgress.xcscheme - - isShown - - - DKNightVersion.xcscheme - - isShown - - - FMDB.xcscheme - - isShown - - - MJExtension.xcscheme - - isShown - - - MJRefresh.xcscheme - - isShown - - - NJKWebViewProgress.xcscheme - - isShown - - - Pods-TTNews.xcscheme - - isShown - - - SDWebImage.xcscheme - - isShown - - - SVProgressHUD.xcscheme - - isShown - - - pop.xcscheme - - isShown - - - - SuppressBuildableAutocreation - - 2F639BAC2BE0057D2AF305181E4637DE - - primary - - - 53406FD23904169597858D7716FC7645 - - primary - - - 63491E1EA0E903734AFD83F4D7DC319A - - primary - - - 8F74D9EA91F4C43190670066BAC37D44 - - primary - - - 928353533005A4198EBDA5B700D37B64 - - primary - - - 9775596B3CA16905FC8F66DA8E2A3C67 - - primary - - - 9EAB3E42EF957541E6C3C76D35FD008E - - primary - - - 9F12FB4A63E20F601CFB2E64A65B57C1 - - primary - - - B4A3E08D49931371F7247521A50EAF79 - - primary - - - B56BC1B5AF7240A3B2E50EC7A163A638 - - primary - - - E96531D2698B334C4DEC89A22248E32D - - primary - - - - - diff --git a/Pods/SDWebImage/LICENSE b/Pods/SDWebImage/LICENSE deleted file mode 100644 index ae783e1..0000000 --- a/Pods/SDWebImage/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2009 Olivier Poitrey - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/Pods/SDWebImage/README.md b/Pods/SDWebImage/README.md deleted file mode 100644 index 80ec254..0000000 --- a/Pods/SDWebImage/README.md +++ /dev/null @@ -1,334 +0,0 @@ -Web Image -========= -[![Build Status](http://img.shields.io/travis/rs/SDWebImage/master.svg?style=flat)](https://travis-ci.org/rs/SDWebImage) -[![Pod Version](http://img.shields.io/cocoapods/v/SDWebImage.svg?style=flat)](http://cocoadocs.org/docsets/SDWebImage/) -[![Pod Platform](http://img.shields.io/cocoapods/p/SDWebImage.svg?style=flat)](http://cocoadocs.org/docsets/SDWebImage/) -[![Pod License](http://img.shields.io/cocoapods/l/SDWebImage.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0.html) -[![Dependency Status](https://www.versioneye.com/objective-c/sdwebimage/3.3/badge.svg?style=flat)](https://www.versioneye.com/objective-c/sdwebimage/3.3) -[![Reference Status](https://www.versioneye.com/objective-c/sdwebimage/reference_badge.svg?style=flat)](https://www.versioneye.com/objective-c/sdwebimage/references) -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/rs/SDWebImage) - -This library provides a category for UIImageView with support for remote images coming from the web. - -It provides: - -- An `UIImageView` category adding web image and cache management to the Cocoa Touch framework -- An asynchronous image downloader -- An asynchronous memory + disk image caching with automatic cache expiration handling -- Animated GIF support -- WebP format support -- A background image decompression -- A guarantee that the same URL won't be downloaded several times -- A guarantee that bogus URLs won't be retried again and again -- A guarantee that main thread will never be blocked -- Performances! -- Use GCD and ARC -- Arm64 support - -NOTE: The version 3.0 of SDWebImage isn't fully backward compatible with 2.0 and requires iOS 5.1.1 -minimum deployment version. If you need iOS < 5.0 support, please use the last [2.0 version](https://github.com/rs/SDWebImage/tree/2.0-compat). - -[How is SDWebImage better than X?](https://github.com/rs/SDWebImage/wiki/How-is-SDWebImage-better-than-X%3F) - -Who Use It ----------- - -Find out [who uses SDWebImage](https://github.com/rs/SDWebImage/wiki/Who-Uses-SDWebImage) and add your app to the list. - -How To Use ----------- - -API documentation is available at [CocoaDocs - SDWebImage](http://cocoadocs.org/docsets/SDWebImage/) - -### Using UIImageView+WebCache category with UITableView - -Just #import the UIImageView+WebCache.h header, and call the sd_setImageWithURL:placeholderImage: -method from the tableView:cellForRowAtIndexPath: UITableViewDataSource method. Everything will be -handled for you, from async downloads to caching management. - -```objective-c -# import - -... - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - static NSString *MyIdentifier = @"MyIdentifier"; - - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; - if (cell == nil) { - cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault - reuseIdentifier:MyIdentifier] autorelease]; - } - - // Here we use the new provided sd_setImageWithURL: method to load the web image - [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] - placeholderImage:[UIImage imageNamed:@"placeholder.png"]]; - - cell.textLabel.text = @"My Text"; - return cell; -} -``` - -### Using blocks - -With blocks, you can be notified about the image download progress and whenever the image retrieval -has completed with success or not: - -```objective-c -// Here we use the new provided sd_setImageWithURL: method to load the web image -[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] - placeholderImage:[UIImage imageNamed:@"placeholder.png"] - completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - ... completion code here ... - }]; -``` - -Note: neither your success nor failure block will be call if your image request is canceled before completion. - -### Using SDWebImageManager - -The SDWebImageManager is the class behind the UIImageView+WebCache category. It ties the -asynchronous downloader with the image cache store. You can use this class directly to benefit -from web image downloading with caching in another context than a UIView (ie: with Cocoa). - -Here is a simple example of how to use SDWebImageManager: - -```objective-c -SDWebImageManager *manager = [SDWebImageManager sharedManager]; -[manager downloadImageWithURL:imageURL - options:0 - progress:^(NSInteger receivedSize, NSInteger expectedSize) { - // progression tracking code - } - completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { - if (image) { - // do something with image - } - }]; -``` - -### Using Asynchronous Image Downloader Independently - -It's also possible to use the async image downloader independently: - -```objective-c -SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader]; -[downloader downloadImageWithURL:imageURL - options:0 - progress:^(NSInteger receivedSize, NSInteger expectedSize) { - // progression tracking code - } - completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) { - if (image && finished) { - // do something with image - } - }]; -``` - -### Using Asynchronous Image Caching Independently - -It is also possible to use the async based image cache store independently. SDImageCache -maintains a memory cache and an optional disk cache. Disk cache write operations are performed -asynchronous so it doesn't add unnecessary latency to the UI. - -The SDImageCache class provides a singleton instance for convenience but you can create your own -instance if you want to create separated cache namespace. - -To lookup the cache, you use the `queryDiskCacheForKey:done:` method. If the method returns nil, it means the cache -doesn't currently own the image. You are thus responsible for generating and caching it. The cache -key is an application unique identifier for the image to cache. It is generally the absolute URL of -the image. - -```objective-c -SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"]; -[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) { - // image is not nil if image was found -}]; -``` - -By default SDImageCache will lookup the disk cache if an image can't be found in the memory cache. -You can prevent this from happening by calling the alternative method `imageFromMemoryCacheForKey:`. - -To store an image into the cache, you use the storeImage:forKey: method: - -```objective-c -[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey]; -``` - -By default, the image will be stored in memory cache as well as on disk cache (asynchronously). If -you want only the memory cache, use the alternative method storeImage:forKey:toDisk: with a negative -third argument. - -### Using cache key filter - -Sometime, you may not want to use the image URL as cache key because part of the URL is dynamic -(i.e.: for access control purpose). SDWebImageManager provides a way to set a cache key filter that -takes the NSURL as input, and output a cache key NSString. - -The following example sets a filter in the application delegate that will remove any query-string from -the URL before to use it as a cache key: - -```objective-c -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - SDWebImageManager.sharedManager.cacheKeyFilter = ^(NSURL *url) { - url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path]; - return [url absoluteString]; - }; - - // Your app init code... - return YES; -} -``` - - -Common Problems ---------------- - -### Using dynamic image size with UITableViewCell - -UITableView determines the size of the image by the first image set for a cell. If your remote images -don't have the same size as your placeholder image, you may experience strange anamorphic scaling issue. -The following article gives a way to workaround this issue: - -[http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/](http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/) - - -### Handle image refresh - -SDWebImage does very aggressive caching by default. It ignores all kind of caching control header returned by the HTTP server and cache the returned images with no time restriction. It implies your images URLs are static URLs pointing to images that never change. If the pointed image happen to change, some parts of the URL should change accordingly. - -If you don't control the image server you're using, you may not be able to change the URL when its content is updated. This is the case for Facebook avatar URLs for instance. In such case, you may use the `SDWebImageRefreshCached` flag. This will slightly degrade the performance but will respect the HTTP caching control headers: - -``` objective-c -[imageView sd_setImageWithURL:[NSURL URLWithString:@"https://graph.facebook.com/olivier.poitrey/picture"] - placeholderImage:[UIImage imageNamed:@"avatar-placeholder.png"] - options:SDWebImageRefreshCached]; -``` - -### Add a progress indicator - -See this category: https://github.com/JJSaccolo/UIActivityIndicator-for-SDWebImage - -Installation ------------- - -There are three ways to use SDWebImage in your project: -- using Cocoapods -- copying all the files into your project -- importing the project as a static library - -### Installation with CocoaPods - -[CocoaPods](http://cocoapods.org/) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries in your projects. See the [Get Started](http://cocoapods.org/#get_started) section for more details. - -#### Podfile -``` -platform :ios, '6.1' -pod 'SDWebImage', '~>3.7' -``` - -If you are using Swift, be sure to add `use_frameworks!` and set your target to iOS 8+: -``` -platform :ios, '8.0' -use_frameworks! -``` - -#### Subspecs - -There are 3 subspecs available now: `Core`, `MapKit` and `WebP` (this means you can install only some of the SDWebImage modules. By default, you get just `Core`, so if you need `WebP`, you need to specify it). - -Podfile example: -``` -pod 'SDWebImage/WebP' -``` - -### Installation with Carthage (iOS 8+) - -[Carthage](https://github.com/Carthage/Carthage) is a lightweight dependency manager for Swift and Objective-C. It leverages CocoaTouch modules and is less invasive than CocoaPods. - -To install with carthage, follow the instruction on [Carthage](https://github.com/Carthage/Carthage) - -#### Cartfile -``` -github "rs/SDWebImage" -``` - -#### Usage -Swift - -If you installed using CocoaPods: -``` -import SDWebImage -``` - -If you installed manually: -``` -import WebImage -``` - -Objective-C - -``` -@import WebImage; -``` - -### Installation by cloning the repository - -In order to gain access to all the files from the repository, you should clone it. -``` -git clone --recursive https://github.com/rs/SDWebImage.git -``` - -### Add the SDWebImage project to your project - -- Download and unzip the last version of the framework from the [download page](https://github.com/rs/SDWebImage/releases) -- Right-click on the project navigator and select "Add Files to "Your Project": -- In the dialog, select SDWebImage.framework: -- Check the "Copy items into destination group's folder (if needed)" checkbox - -### Add dependencies - -- In you application project app’s target settings, find the "Build Phases" section and open the "Link Binary With Libraries" block: -- Click the "+" button again and select the "ImageIO.framework", this is needed by the progressive download feature: - -### Add Linker Flag - -Open the "Build Settings" tab, in the "Linking" section, locate the "Other Linker Flags" setting and add the "-ObjC" flag: - -![Other Linker Flags](http://dl.dropbox.com/u/123346/SDWebImage/10_other_linker_flags.jpg) - -Alternatively, if this causes compilation problems with frameworks that extend optional libraries, such as Parse, RestKit or opencv2, instead of the -ObjC flag use: -``` --force_load SDWebImage.framework/Versions/Current/SDWebImage -``` - -If you're using Cocoa Pods and have any frameworks that extend optional libraries, such as Parsen RestKit or opencv2, instead of the -ObjC flag use: -``` --force_load $(TARGET_BUILD_DIR)/libPods.a -``` -and this: -``` -$(inherited) -``` - -### Import headers in your source files - -In the source files where you need to use the library, import the header file: - -```objective-c -# import -``` - -### Build Project - -At this point your workspace should build without error. If you are having problem, post to the Issue and the -community can help you solve it. - -Future Enhancements -------------------- - -- LRU memory cache cleanup instead of reset on memory warning - -## Licenses - -All source code is licensed under the [MIT License](https://raw.github.com/rs/SDWebImage/master/LICENSE). diff --git a/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.h b/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.h deleted file mode 100644 index 69c76dc..0000000 --- a/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by Fabrice Aneche on 06/01/14. -// Copyright (c) 2014 Dailymotion. All rights reserved. -// - -#import - -@interface NSData (ImageContentType) - -/** - * Compute the content type for an image data - * - * @param data the input data - * - * @return the content type as string (i.e. image/jpeg, image/gif) - */ -+ (NSString *)sd_contentTypeForImageData:(NSData *)data; - -@end - - -@interface NSData (ImageContentTypeDeprecated) - -+ (NSString *)contentTypeForImageData:(NSData *)data __deprecated_msg("Use `sd_contentTypeForImageData:`"); - -@end diff --git a/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.m b/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.m deleted file mode 100644 index 0941cfa..0000000 --- a/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.m +++ /dev/null @@ -1,49 +0,0 @@ -// -// Created by Fabrice Aneche on 06/01/14. -// Copyright (c) 2014 Dailymotion. All rights reserved. -// - -#import "NSData+ImageContentType.h" - - -@implementation NSData (ImageContentType) - -+ (NSString *)sd_contentTypeForImageData:(NSData *)data { - uint8_t c; - [data getBytes:&c length:1]; - switch (c) { - case 0xFF: - return @"image/jpeg"; - case 0x89: - return @"image/png"; - case 0x47: - return @"image/gif"; - case 0x49: - case 0x4D: - return @"image/tiff"; - case 0x52: - // R as RIFF for WEBP - if ([data length] < 12) { - return nil; - } - - NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding]; - if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) { - return @"image/webp"; - } - - return nil; - } - return nil; -} - -@end - - -@implementation NSData (ImageContentTypeDeprecated) - -+ (NSString *)contentTypeForImageData:(NSData *)data { - return [self sd_contentTypeForImageData:data]; -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDImageCache.h b/Pods/SDWebImage/SDWebImage/SDImageCache.h deleted file mode 100644 index 9577726..0000000 --- a/Pods/SDWebImage/SDWebImage/SDImageCache.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import -#import "SDWebImageCompat.h" - -typedef NS_ENUM(NSInteger, SDImageCacheType) { - /** - * The image wasn't available the SDWebImage caches, but was downloaded from the web. - */ - SDImageCacheTypeNone, - /** - * The image was obtained from the disk cache. - */ - SDImageCacheTypeDisk, - /** - * The image was obtained from the memory cache. - */ - SDImageCacheTypeMemory -}; - -typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType); - -typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache); - -typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize); - -/** - * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed - * asynchronous so it doesn’t add unnecessary latency to the UI. - */ -@interface SDImageCache : NSObject - -/** - * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory. - * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption. - */ -@property (assign, nonatomic) BOOL shouldDecompressImages; - -/** - * disable iCloud backup [defaults to YES] - */ -@property (assign, nonatomic) BOOL shouldDisableiCloud; - -/** - * use memory cache [defaults to YES] - */ -@property (assign, nonatomic) BOOL shouldCacheImagesInMemory; - -/** - * The maximum "total cost" of the in-memory image cache. The cost function is the number of pixels held in memory. - */ -@property (assign, nonatomic) NSUInteger maxMemoryCost; - -/** - * The maximum number of objects the cache should hold. - */ -@property (assign, nonatomic) NSUInteger maxMemoryCountLimit; - -/** - * The maximum length of time to keep an image in the cache, in seconds - */ -@property (assign, nonatomic) NSInteger maxCacheAge; - -/** - * The maximum size of the cache, in bytes. - */ -@property (assign, nonatomic) NSUInteger maxCacheSize; - -/** - * Returns global shared cache instance - * - * @return SDImageCache global instance - */ -+ (SDImageCache *)sharedImageCache; - -/** - * Init a new cache store with a specific namespace - * - * @param ns The namespace to use for this cache store - */ -- (id)initWithNamespace:(NSString *)ns; - -/** - * Init a new cache store with a specific namespace and directory - * - * @param ns The namespace to use for this cache store - * @param directory Directory to cache disk images in - */ -- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory; - --(NSString *)makeDiskCachePath:(NSString*)fullNamespace; - -/** - * Add a read-only cache path to search for images pre-cached by SDImageCache - * Useful if you want to bundle pre-loaded images with your app - * - * @param path The path to use for this read-only cache path - */ -- (void)addReadOnlyCachePath:(NSString *)path; - -/** - * Store an image into memory and disk cache at the given key. - * - * @param image The image to store - * @param key The unique image cache key, usually it's image absolute URL - */ -- (void)storeImage:(UIImage *)image forKey:(NSString *)key; - -/** - * Store an image into memory and optionally disk cache at the given key. - * - * @param image The image to store - * @param key The unique image cache key, usually it's image absolute URL - * @param toDisk Store the image to disk cache if YES - */ -- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk; - -/** - * Store an image into memory and optionally disk cache at the given key. - * - * @param image The image to store - * @param recalculate BOOL indicates if imageData can be used or a new data should be constructed from the UIImage - * @param imageData The image data as returned by the server, this representation will be used for disk storage - * instead of converting the given image object into a storable/compressed image format in order - * to save quality and CPU - * @param key The unique image cache key, usually it's image absolute URL - * @param toDisk Store the image to disk cache if YES - */ -- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk; - -/** - * Query the disk cache asynchronously. - * - * @param key The unique key used to store the wanted image - */ -- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock; - -/** - * Query the memory cache synchronously. - * - * @param key The unique key used to store the wanted image - */ -- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key; - -/** - * Query the disk cache synchronously after checking the memory cache. - * - * @param key The unique key used to store the wanted image - */ -- (UIImage *)imageFromDiskCacheForKey:(NSString *)key; - -/** - * Remove the image from memory and disk cache synchronously - * - * @param key The unique image cache key - */ -- (void)removeImageForKey:(NSString *)key; - - -/** - * Remove the image from memory and disk cache asynchronously - * - * @param key The unique image cache key - * @param completion An block that should be executed after the image has been removed (optional) - */ -- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion; - -/** - * Remove the image from memory and optionally disk cache asynchronously - * - * @param key The unique image cache key - * @param fromDisk Also remove cache entry from disk if YES - */ -- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk; - -/** - * Remove the image from memory and optionally disk cache asynchronously - * - * @param key The unique image cache key - * @param fromDisk Also remove cache entry from disk if YES - * @param completion An block that should be executed after the image has been removed (optional) - */ -- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion; - -/** - * Clear all memory cached images - */ -- (void)clearMemory; - -/** - * Clear all disk cached images. Non-blocking method - returns immediately. - * @param completion An block that should be executed after cache expiration completes (optional) - */ -- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion; - -/** - * Clear all disk cached images - * @see clearDiskOnCompletion: - */ -- (void)clearDisk; - -/** - * Remove all expired cached image from disk. Non-blocking method - returns immediately. - * @param completionBlock An block that should be executed after cache expiration completes (optional) - */ -- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock; - -/** - * Remove all expired cached image from disk - * @see cleanDiskWithCompletionBlock: - */ -- (void)cleanDisk; - -/** - * Get the size used by the disk cache - */ -- (NSUInteger)getSize; - -/** - * Get the number of images in the disk cache - */ -- (NSUInteger)getDiskCount; - -/** - * Asynchronously calculate the disk cache's size. - */ -- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock; - -/** - * Async check if image exists in disk cache already (does not load the image) - * - * @param key the key describing the url - * @param completionBlock the block to be executed when the check is done. - * @note the completion block will be always executed on the main queue - */ -- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock; - -/** - * Check if image exists in disk cache already (does not load the image) - * - * @param key the key describing the url - * - * @return YES if an image exists for the given key - */ -- (BOOL)diskImageExistsWithKey:(NSString *)key; - -/** - * Get the cache path for a certain key (needs the cache path root folder) - * - * @param key the key (can be obtained from url using cacheKeyForURL) - * @param path the cache path root folder - * - * @return the cache path - */ -- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path; - -/** - * Get the default cache path for a certain key - * - * @param key the key (can be obtained from url using cacheKeyForURL) - * - * @return the default cache path - */ -- (NSString *)defaultCachePathForKey:(NSString *)key; - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDImageCache.m b/Pods/SDWebImage/SDWebImage/SDImageCache.m deleted file mode 100644 index aa0ff6c..0000000 --- a/Pods/SDWebImage/SDWebImage/SDImageCache.m +++ /dev/null @@ -1,650 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "SDImageCache.h" -#import "SDWebImageDecoder.h" -#import "UIImage+MultiFormat.h" -#import - -// See https://github.com/rs/SDWebImage/pull/1141 for discussion -@interface AutoPurgeCache : NSCache -@end - -@implementation AutoPurgeCache - -- (id)init -{ - self = [super init]; - if (self) { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; - } - return self; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; - -} - -@end - -static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week -// PNG signature bytes and data (below) -static unsigned char kPNGSignatureBytes[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; -static NSData *kPNGSignatureData = nil; - -BOOL ImageDataHasPNGPreffix(NSData *data); - -BOOL ImageDataHasPNGPreffix(NSData *data) { - NSUInteger pngSignatureLength = [kPNGSignatureData length]; - if ([data length] >= pngSignatureLength) { - if ([[data subdataWithRange:NSMakeRange(0, pngSignatureLength)] isEqualToData:kPNGSignatureData]) { - return YES; - } - } - - return NO; -} - -FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { - return image.size.height * image.size.width * image.scale * image.scale; -} - -@interface SDImageCache () - -@property (strong, nonatomic) NSCache *memCache; -@property (strong, nonatomic) NSString *diskCachePath; -@property (strong, nonatomic) NSMutableArray *customPaths; -@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue; - -@end - - -@implementation SDImageCache { - NSFileManager *_fileManager; -} - -+ (SDImageCache *)sharedImageCache { - static dispatch_once_t once; - static id instance; - dispatch_once(&once, ^{ - instance = [self new]; - }); - return instance; -} - -- (id)init { - return [self initWithNamespace:@"default"]; -} - -- (id)initWithNamespace:(NSString *)ns { - NSString *path = [self makeDiskCachePath:ns]; - return [self initWithNamespace:ns diskCacheDirectory:path]; -} - -- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory { - if ((self = [super init])) { - NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns]; - - // initialise PNG signature data - kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8]; - - // Create IO serial queue - _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL); - - // Init default values - _maxCacheAge = kDefaultCacheMaxCacheAge; - - // Init the memory cache - _memCache = [[AutoPurgeCache alloc] init]; - _memCache.name = fullNamespace; - - // Init the disk cache - if (directory != nil) { - _diskCachePath = [directory stringByAppendingPathComponent:fullNamespace]; - } else { - NSString *path = [self makeDiskCachePath:ns]; - _diskCachePath = path; - } - - // Set decompression to YES - _shouldDecompressImages = YES; - - // memory cache enabled - _shouldCacheImagesInMemory = YES; - - // Disable iCloud - _shouldDisableiCloud = YES; - - dispatch_sync(_ioQueue, ^{ - _fileManager = [NSFileManager new]; - }); - -#if TARGET_OS_IPHONE - // Subscribe to app events - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(clearMemory) - name:UIApplicationDidReceiveMemoryWarningNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(cleanDisk) - name:UIApplicationWillTerminateNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(backgroundCleanDisk) - name:UIApplicationDidEnterBackgroundNotification - object:nil]; -#endif - } - - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - SDDispatchQueueRelease(_ioQueue); -} - -- (void)addReadOnlyCachePath:(NSString *)path { - if (!self.customPaths) { - self.customPaths = [NSMutableArray new]; - } - - if (![self.customPaths containsObject:path]) { - [self.customPaths addObject:path]; - } -} - -- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path { - NSString *filename = [self cachedFileNameForKey:key]; - return [path stringByAppendingPathComponent:filename]; -} - -- (NSString *)defaultCachePathForKey:(NSString *)key { - return [self cachePathForKey:key inPath:self.diskCachePath]; -} - -#pragma mark SDImageCache (private) - -- (NSString *)cachedFileNameForKey:(NSString *)key { - const char *str = [key UTF8String]; - if (str == NULL) { - str = ""; - } - unsigned char r[CC_MD5_DIGEST_LENGTH]; - CC_MD5(str, (CC_LONG)strlen(str), r); - NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@", - r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], - r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]]; - - return filename; -} - -#pragma mark ImageCache - -// Init the disk cache --(NSString *)makeDiskCachePath:(NSString*)fullNamespace{ - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - return [paths[0] stringByAppendingPathComponent:fullNamespace]; -} - -- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk { - if (!image || !key) { - return; - } - // if memory cache is enabled - if (self.shouldCacheImagesInMemory) { - NSUInteger cost = SDCacheCostForImage(image); - [self.memCache setObject:image forKey:key cost:cost]; - } - - if (toDisk) { - dispatch_async(self.ioQueue, ^{ - NSData *data = imageData; - - if (image && (recalculate || !data)) { -#if TARGET_OS_IPHONE - // We need to determine if the image is a PNG or a JPEG - // PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html) - // The first eight bytes of a PNG file always contain the following (decimal) values: - // 137 80 78 71 13 10 26 10 - - // If the imageData is nil (i.e. if trying to save a UIImage directly or the image was transformed on download) - // and the image has an alpha channel, we will consider it PNG to avoid losing the transparency - int alphaInfo = CGImageGetAlphaInfo(image.CGImage); - BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone || - alphaInfo == kCGImageAlphaNoneSkipFirst || - alphaInfo == kCGImageAlphaNoneSkipLast); - BOOL imageIsPng = hasAlpha; - - // But if we have an image data, we will look at the preffix - if ([imageData length] >= [kPNGSignatureData length]) { - imageIsPng = ImageDataHasPNGPreffix(imageData); - } - - if (imageIsPng) { - data = UIImagePNGRepresentation(image); - } - else { - data = UIImageJPEGRepresentation(image, (CGFloat)1.0); - } -#else - data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil]; -#endif - } - - if (data) { - if (![_fileManager fileExistsAtPath:_diskCachePath]) { - [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL]; - } - - // get cache Path for image key - NSString *cachePathForKey = [self defaultCachePathForKey:key]; - // transform to NSUrl - NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey]; - - [_fileManager createFileAtPath:cachePathForKey contents:data attributes:nil]; - - // disable iCloud backup - if (self.shouldDisableiCloud) { - [fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil]; - } - } - }); - } -} - -- (void)storeImage:(UIImage *)image forKey:(NSString *)key { - [self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:YES]; -} - -- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk { - [self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:toDisk]; -} - -- (BOOL)diskImageExistsWithKey:(NSString *)key { - BOOL exists = NO; - - // this is an exception to access the filemanager on another queue than ioQueue, but we are using the shared instance - // from apple docs on NSFileManager: The methods of the shared NSFileManager object can be called from multiple threads safely. - exists = [[NSFileManager defaultManager] fileExistsAtPath:[self defaultCachePathForKey:key]]; - - // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name - // checking the key with and without the extension - if (!exists) { - exists = [[NSFileManager defaultManager] fileExistsAtPath:[[self defaultCachePathForKey:key] stringByDeletingPathExtension]]; - } - - return exists; -} - -- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock { - dispatch_async(_ioQueue, ^{ - BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]]; - - // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name - // checking the key with and without the extension - if (!exists) { - exists = [_fileManager fileExistsAtPath:[[self defaultCachePathForKey:key] stringByDeletingPathExtension]]; - } - - if (completionBlock) { - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(exists); - }); - } - }); -} - -- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key { - return [self.memCache objectForKey:key]; -} - -- (UIImage *)imageFromDiskCacheForKey:(NSString *)key { - - // First check the in-memory cache... - UIImage *image = [self imageFromMemoryCacheForKey:key]; - if (image) { - return image; - } - - // Second check the disk cache... - UIImage *diskImage = [self diskImageForKey:key]; - if (diskImage && self.shouldCacheImagesInMemory) { - NSUInteger cost = SDCacheCostForImage(diskImage); - [self.memCache setObject:diskImage forKey:key cost:cost]; - } - - return diskImage; -} - -- (NSData *)diskImageDataBySearchingAllPathsForKey:(NSString *)key { - NSString *defaultPath = [self defaultCachePathForKey:key]; - NSData *data = [NSData dataWithContentsOfFile:defaultPath]; - if (data) { - return data; - } - - // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name - // checking the key with and without the extension - data = [NSData dataWithContentsOfFile:[defaultPath stringByDeletingPathExtension]]; - if (data) { - return data; - } - - NSArray *customPaths = [self.customPaths copy]; - for (NSString *path in customPaths) { - NSString *filePath = [self cachePathForKey:key inPath:path]; - NSData *imageData = [NSData dataWithContentsOfFile:filePath]; - if (imageData) { - return imageData; - } - - // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name - // checking the key with and without the extension - imageData = [NSData dataWithContentsOfFile:[filePath stringByDeletingPathExtension]]; - if (imageData) { - return imageData; - } - } - - return nil; -} - -- (UIImage *)diskImageForKey:(NSString *)key { - NSData *data = [self diskImageDataBySearchingAllPathsForKey:key]; - if (data) { - UIImage *image = [UIImage sd_imageWithData:data]; - image = [self scaledImageForKey:key image:image]; - if (self.shouldDecompressImages) { - image = [UIImage decodedImageWithImage:image]; - } - return image; - } - else { - return nil; - } -} - -- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image { - return SDScaledImageForKey(key, image); -} - -- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock { - if (!doneBlock) { - return nil; - } - - if (!key) { - doneBlock(nil, SDImageCacheTypeNone); - return nil; - } - - // First check the in-memory cache... - UIImage *image = [self imageFromMemoryCacheForKey:key]; - if (image) { - doneBlock(image, SDImageCacheTypeMemory); - return nil; - } - - NSOperation *operation = [NSOperation new]; - dispatch_async(self.ioQueue, ^{ - if (operation.isCancelled) { - return; - } - - @autoreleasepool { - UIImage *diskImage = [self diskImageForKey:key]; - if (diskImage && self.shouldCacheImagesInMemory) { - NSUInteger cost = SDCacheCostForImage(diskImage); - [self.memCache setObject:diskImage forKey:key cost:cost]; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - doneBlock(diskImage, SDImageCacheTypeDisk); - }); - } - }); - - return operation; -} - -- (void)removeImageForKey:(NSString *)key { - [self removeImageForKey:key withCompletion:nil]; -} - -- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion { - [self removeImageForKey:key fromDisk:YES withCompletion:completion]; -} - -- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk { - [self removeImageForKey:key fromDisk:fromDisk withCompletion:nil]; -} - -- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion { - - if (key == nil) { - return; - } - - if (self.shouldCacheImagesInMemory) { - [self.memCache removeObjectForKey:key]; - } - - if (fromDisk) { - dispatch_async(self.ioQueue, ^{ - [_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil]; - - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(); - }); - } - }); - } else if (completion){ - completion(); - } - -} - -- (void)setMaxMemoryCost:(NSUInteger)maxMemoryCost { - self.memCache.totalCostLimit = maxMemoryCost; -} - -- (NSUInteger)maxMemoryCost { - return self.memCache.totalCostLimit; -} - -- (NSUInteger)maxMemoryCountLimit { - return self.memCache.countLimit; -} - -- (void)setMaxMemoryCountLimit:(NSUInteger)maxCountLimit { - self.memCache.countLimit = maxCountLimit; -} - -- (void)clearMemory { - [self.memCache removeAllObjects]; -} - -- (void)clearDisk { - [self clearDiskOnCompletion:nil]; -} - -- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion -{ - dispatch_async(self.ioQueue, ^{ - [_fileManager removeItemAtPath:self.diskCachePath error:nil]; - [_fileManager createDirectoryAtPath:self.diskCachePath - withIntermediateDirectories:YES - attributes:nil - error:NULL]; - - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(); - }); - } - }); -} - -- (void)cleanDisk { - [self cleanDiskWithCompletionBlock:nil]; -} - -- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock { - dispatch_async(self.ioQueue, ^{ - NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES]; - NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey]; - - // This enumerator prefetches useful properties for our cache files. - NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL - includingPropertiesForKeys:resourceKeys - options:NSDirectoryEnumerationSkipsHiddenFiles - errorHandler:NULL]; - - NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge]; - NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary]; - NSUInteger currentCacheSize = 0; - - // Enumerate all of the files in the cache directory. This loop has two purposes: - // - // 1. Removing files that are older than the expiration date. - // 2. Storing file attributes for the size-based cleanup pass. - NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init]; - for (NSURL *fileURL in fileEnumerator) { - NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL]; - - // Skip directories. - if ([resourceValues[NSURLIsDirectoryKey] boolValue]) { - continue; - } - - // Remove files that are older than the expiration date; - NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey]; - if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) { - [urlsToDelete addObject:fileURL]; - continue; - } - - // Store a reference to this file and account for its total size. - NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey]; - currentCacheSize += [totalAllocatedSize unsignedIntegerValue]; - [cacheFiles setObject:resourceValues forKey:fileURL]; - } - - for (NSURL *fileURL in urlsToDelete) { - [_fileManager removeItemAtURL:fileURL error:nil]; - } - - // If our remaining disk cache exceeds a configured maximum size, perform a second - // size-based cleanup pass. We delete the oldest files first. - if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) { - // Target half of our maximum cache size for this cleanup pass. - const NSUInteger desiredCacheSize = self.maxCacheSize / 2; - - // Sort the remaining cache files by their last modification time (oldest first). - NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent - usingComparator:^NSComparisonResult(id obj1, id obj2) { - return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]]; - }]; - - // Delete files until we fall below our desired cache size. - for (NSURL *fileURL in sortedFiles) { - if ([_fileManager removeItemAtURL:fileURL error:nil]) { - NSDictionary *resourceValues = cacheFiles[fileURL]; - NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey]; - currentCacheSize -= [totalAllocatedSize unsignedIntegerValue]; - - if (currentCacheSize < desiredCacheSize) { - break; - } - } - } - } - if (completionBlock) { - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(); - }); - } - }); -} - -- (void)backgroundCleanDisk { - Class UIApplicationClass = NSClassFromString(@"UIApplication"); - if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) { - return; - } - UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)]; - __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{ - // Clean up any unfinished task business by marking where you - // stopped or ending the task outright. - [application endBackgroundTask:bgTask]; - bgTask = UIBackgroundTaskInvalid; - }]; - - // Start the long-running task and return immediately. - [self cleanDiskWithCompletionBlock:^{ - [application endBackgroundTask:bgTask]; - bgTask = UIBackgroundTaskInvalid; - }]; -} - -- (NSUInteger)getSize { - __block NSUInteger size = 0; - dispatch_sync(self.ioQueue, ^{ - NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath]; - for (NSString *fileName in fileEnumerator) { - NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName]; - NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; - size += [attrs fileSize]; - } - }); - return size; -} - -- (NSUInteger)getDiskCount { - __block NSUInteger count = 0; - dispatch_sync(self.ioQueue, ^{ - NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath]; - count = [[fileEnumerator allObjects] count]; - }); - return count; -} - -- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock { - NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES]; - - dispatch_async(self.ioQueue, ^{ - NSUInteger fileCount = 0; - NSUInteger totalSize = 0; - - NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL - includingPropertiesForKeys:@[NSFileSize] - options:NSDirectoryEnumerationSkipsHiddenFiles - errorHandler:NULL]; - - for (NSURL *fileURL in fileEnumerator) { - NSNumber *fileSize; - [fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL]; - totalSize += [fileSize unsignedIntegerValue]; - fileCount += 1; - } - - if (completionBlock) { - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(fileCount, totalSize); - }); - } - }); -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageCompat.h b/Pods/SDWebImage/SDWebImage/SDWebImageCompat.h deleted file mode 100644 index 3c21b41..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageCompat.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * (c) Jamie Pinkham - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import - -#ifdef __OBJC_GC__ -#error SDWebImage does not support Objective-C Garbage Collection -#endif - -#if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0 -#error SDWebImage doesn't support Deployment Target version < 5.0 -#endif - -#if !TARGET_OS_IPHONE -#import -#ifndef UIImage -#define UIImage NSImage -#endif -#ifndef UIImageView -#define UIImageView NSImageView -#endif -#else - -#import - -#endif - -#ifndef NS_ENUM -#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type -#endif - -#ifndef NS_OPTIONS -#define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type -#endif - -#if OS_OBJECT_USE_OBJC - #undef SDDispatchQueueRelease - #undef SDDispatchQueueSetterSementics - #define SDDispatchQueueRelease(q) - #define SDDispatchQueueSetterSementics strong -#else -#undef SDDispatchQueueRelease -#undef SDDispatchQueueSetterSementics -#define SDDispatchQueueRelease(q) (dispatch_release(q)) -#define SDDispatchQueueSetterSementics assign -#endif - -extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image); - -typedef void(^SDWebImageNoParamsBlock)(); - -extern NSString *const SDWebImageErrorDomain; - -#define dispatch_main_sync_safe(block)\ - if ([NSThread isMainThread]) {\ - block();\ - } else {\ - dispatch_sync(dispatch_get_main_queue(), block);\ - } - -#define dispatch_main_async_safe(block)\ - if ([NSThread isMainThread]) {\ - block();\ - } else {\ - dispatch_async(dispatch_get_main_queue(), block);\ - } diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageCompat.m b/Pods/SDWebImage/SDWebImage/SDWebImageCompat.m deleted file mode 100644 index 9a011bc..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageCompat.m +++ /dev/null @@ -1,51 +0,0 @@ -// -// SDWebImageCompat.m -// SDWebImage -// -// Created by Olivier Poitrey on 11/12/12. -// Copyright (c) 2012 Dailymotion. All rights reserved. -// - -#import "SDWebImageCompat.h" - -#if !__has_feature(objc_arc) -#error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag -#endif - -inline UIImage *SDScaledImageForKey(NSString *key, UIImage *image) { - if (!image) { - return nil; - } - - if ([image.images count] > 0) { - NSMutableArray *scaledImages = [NSMutableArray array]; - - for (UIImage *tempImage in image.images) { - [scaledImages addObject:SDScaledImageForKey(key, tempImage)]; - } - - return [UIImage animatedImageWithImages:scaledImages duration:image.duration]; - } - else { - if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) { - CGFloat scale = [UIScreen mainScreen].scale; - if (key.length >= 8) { - NSRange range = [key rangeOfString:@"@2x."]; - if (range.location != NSNotFound) { - scale = 2.0; - } - - range = [key rangeOfString:@"@3x."]; - if (range.location != NSNotFound) { - scale = 3.0; - } - } - - UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; - image = scaledImage; - } - return image; - } -} - -NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain"; diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.h b/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.h deleted file mode 100644 index 0176a7b..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * Created by james on 9/28/11. - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import -#import "SDWebImageCompat.h" - -@interface UIImage (ForceDecode) - -+ (UIImage *)decodedImageWithImage:(UIImage *)image; - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.m b/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.m deleted file mode 100644 index a7c0246..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.m +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * Created by james on 9/28/11. - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "SDWebImageDecoder.h" - -@implementation UIImage (ForceDecode) - -+ (UIImage *)decodedImageWithImage:(UIImage *)image { - // while downloading huge amount of images - // autorelease the bitmap context - // and all vars to help system to free memory - // when there are memory warning. - // on iOS7, do not forget to call - // [[SDImageCache sharedImageCache] clearMemory]; - @autoreleasepool{ - // do not decode animated images - if (image.images) { return image; } - - CGImageRef imageRef = image.CGImage; - - CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef); - BOOL anyAlpha = (alpha == kCGImageAlphaFirst || - alpha == kCGImageAlphaLast || - alpha == kCGImageAlphaPremultipliedFirst || - alpha == kCGImageAlphaPremultipliedLast); - - if (anyAlpha) { return image; } - - size_t width = CGImageGetWidth(imageRef); - size_t height = CGImageGetHeight(imageRef); - - // current - CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef)); - CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef); - - bool unsupportedColorSpace = (imageColorSpaceModel == 0 || imageColorSpaceModel == -1 || imageColorSpaceModel == kCGColorSpaceModelCMYK || imageColorSpaceModel == kCGColorSpaceModelIndexed); - if (unsupportedColorSpace) - colorspaceRef = CGColorSpaceCreateDeviceRGB(); - - CGContextRef context = CGBitmapContextCreate(NULL, width, - height, - CGImageGetBitsPerComponent(imageRef), - 0, - colorspaceRef, - kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); - - // Draw the image into the context and retrieve the new image, which will now have an alpha layer - CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); - CGImageRef imageRefWithAlpha = CGBitmapContextCreateImage(context); - UIImage *imageWithAlpha = [UIImage imageWithCGImage:imageRefWithAlpha scale:image.scale orientation:image.imageOrientation]; - - if (unsupportedColorSpace) - CGColorSpaceRelease(colorspaceRef); - - CGContextRelease(context); - CGImageRelease(imageRefWithAlpha); - - return imageWithAlpha; - } -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.h b/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.h deleted file mode 100644 index b64fb13..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import -#import "SDWebImageCompat.h" -#import "SDWebImageOperation.h" - -typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) { - SDWebImageDownloaderLowPriority = 1 << 0, - SDWebImageDownloaderProgressiveDownload = 1 << 1, - - /** - * By default, request prevent the of NSURLCache. With this flag, NSURLCache - * is used with default policies. - */ - SDWebImageDownloaderUseNSURLCache = 1 << 2, - - /** - * Call completion block with nil image/imageData if the image was read from NSURLCache - * (to be combined with `SDWebImageDownloaderUseNSURLCache`). - */ - - SDWebImageDownloaderIgnoreCachedResponse = 1 << 3, - /** - * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for - * extra time in background to let the request finish. If the background task expires the operation will be cancelled. - */ - - SDWebImageDownloaderContinueInBackground = 1 << 4, - - /** - * Handles cookies stored in NSHTTPCookieStore by setting - * NSMutableURLRequest.HTTPShouldHandleCookies = YES; - */ - SDWebImageDownloaderHandleCookies = 1 << 5, - - /** - * Enable to allow untrusted SSL certificates. - * Useful for testing purposes. Use with caution in production. - */ - SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6, - - /** - * Put the image in the high priority queue. - */ - SDWebImageDownloaderHighPriority = 1 << 7, -}; - -typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) { - /** - * Default value. All download operations will execute in queue style (first-in-first-out). - */ - SDWebImageDownloaderFIFOExecutionOrder, - - /** - * All download operations will execute in stack style (last-in-first-out). - */ - SDWebImageDownloaderLIFOExecutionOrder -}; - -extern NSString *const SDWebImageDownloadStartNotification; -extern NSString *const SDWebImageDownloadStopNotification; - -typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize); - -typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage *image, NSData *data, NSError *error, BOOL finished); - -typedef NSDictionary *(^SDWebImageDownloaderHeadersFilterBlock)(NSURL *url, NSDictionary *headers); - -/** - * Asynchronous downloader dedicated and optimized for image loading. - */ -@interface SDWebImageDownloader : NSObject - -/** - * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory. - * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption. - */ -@property (assign, nonatomic) BOOL shouldDecompressImages; - -@property (assign, nonatomic) NSInteger maxConcurrentDownloads; - -/** - * Shows the current amount of downloads that still need to be downloaded - */ -@property (readonly, nonatomic) NSUInteger currentDownloadCount; - - -/** - * The timeout value (in seconds) for the download operation. Default: 15.0. - */ -@property (assign, nonatomic) NSTimeInterval downloadTimeout; - - -/** - * Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`. - */ -@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder; - -/** - * Singleton method, returns the shared instance - * - * @return global shared instance of downloader class - */ -+ (SDWebImageDownloader *)sharedDownloader; - -/** - * Set the default URL credential to be set for request operations. - */ -@property (strong, nonatomic) NSURLCredential *urlCredential; - -/** - * Set username - */ -@property (strong, nonatomic) NSString *username; - -/** - * Set password - */ -@property (strong, nonatomic) NSString *password; - -/** - * Set filter to pick headers for downloading image HTTP request. - * - * This block will be invoked for each downloading image request, returned - * NSDictionary will be used as headers in corresponding HTTP request. - */ -@property (nonatomic, copy) SDWebImageDownloaderHeadersFilterBlock headersFilter; - -/** - * Set a value for a HTTP header to be appended to each download HTTP request. - * - * @param value The value for the header field. Use `nil` value to remove the header. - * @param field The name of the header field to set. - */ -- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field; - -/** - * Returns the value of the specified HTTP header field. - * - * @return The value associated with the header field field, or `nil` if there is no corresponding header field. - */ -- (NSString *)valueForHTTPHeaderField:(NSString *)field; - -/** - * Sets a subclass of `SDWebImageDownloaderOperation` as the default - * `NSOperation` to be used each time SDWebImage constructs a request - * operation to download an image. - * - * @param operationClass The subclass of `SDWebImageDownloaderOperation` to set - * as default. Passing `nil` will revert to `SDWebImageDownloaderOperation`. - */ -- (void)setOperationClass:(Class)operationClass; - -/** - * Creates a SDWebImageDownloader async downloader instance with a given URL - * - * The delegate will be informed when the image is finish downloaded or an error has happen. - * - * @see SDWebImageDownloaderDelegate - * - * @param url The URL to the image to download - * @param options The options to be used for this download - * @param progressBlock A block called repeatedly while the image is downloading - * @param completedBlock A block called once the download is completed. - * If the download succeeded, the image parameter is set, in case of error, - * error parameter is set with the error. The last parameter is always YES - * if SDWebImageDownloaderProgressiveDownload isn't use. With the - * SDWebImageDownloaderProgressiveDownload option, this block is called - * repeatedly with the partial image object and the finished argument set to NO - * before to be called a last time with the full image and finished argument - * set to YES. In case of error, the finished argument is always YES. - * - * @return A cancellable SDWebImageOperation - */ -- (id )downloadImageWithURL:(NSURL *)url - options:(SDWebImageDownloaderOptions)options - progress:(SDWebImageDownloaderProgressBlock)progressBlock - completed:(SDWebImageDownloaderCompletedBlock)completedBlock; - -/** - * Sets the download queue suspension state - */ -- (void)setSuspended:(BOOL)suspended; - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.m b/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.m deleted file mode 100644 index 1fdcfc9..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.m +++ /dev/null @@ -1,232 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "SDWebImageDownloader.h" -#import "SDWebImageDownloaderOperation.h" -#import - -static NSString *const kProgressCallbackKey = @"progress"; -static NSString *const kCompletedCallbackKey = @"completed"; - -@interface SDWebImageDownloader () - -@property (strong, nonatomic) NSOperationQueue *downloadQueue; -@property (weak, nonatomic) NSOperation *lastAddedOperation; -@property (assign, nonatomic) Class operationClass; -@property (strong, nonatomic) NSMutableDictionary *URLCallbacks; -@property (strong, nonatomic) NSMutableDictionary *HTTPHeaders; -// This queue is used to serialize the handling of the network responses of all the download operation in a single queue -@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue; - -@end - -@implementation SDWebImageDownloader - -+ (void)initialize { - // Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator ) - // To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import - if (NSClassFromString(@"SDNetworkActivityIndicator")) { - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")]; -#pragma clang diagnostic pop - - // Remove observer in case it was previously added. - [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStartNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStopNotification object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:activityIndicator - selector:NSSelectorFromString(@"startActivity") - name:SDWebImageDownloadStartNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:activityIndicator - selector:NSSelectorFromString(@"stopActivity") - name:SDWebImageDownloadStopNotification object:nil]; - } -} - -+ (SDWebImageDownloader *)sharedDownloader { - static dispatch_once_t once; - static id instance; - dispatch_once(&once, ^{ - instance = [self new]; - }); - return instance; -} - -- (id)init { - if ((self = [super init])) { - _operationClass = [SDWebImageDownloaderOperation class]; - _shouldDecompressImages = YES; - _executionOrder = SDWebImageDownloaderFIFOExecutionOrder; - _downloadQueue = [NSOperationQueue new]; - _downloadQueue.maxConcurrentOperationCount = 6; - _URLCallbacks = [NSMutableDictionary new]; -#ifdef SD_WEBP - _HTTPHeaders = [@{@"Accept": @"image/webp,image/*;q=0.8"} mutableCopy]; -#else - _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy]; -#endif - _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); - _downloadTimeout = 15.0; - } - return self; -} - -- (void)dealloc { - [self.downloadQueue cancelAllOperations]; - SDDispatchQueueRelease(_barrierQueue); -} - -- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field { - if (value) { - self.HTTPHeaders[field] = value; - } - else { - [self.HTTPHeaders removeObjectForKey:field]; - } -} - -- (NSString *)valueForHTTPHeaderField:(NSString *)field { - return self.HTTPHeaders[field]; -} - -- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads { - _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads; -} - -- (NSUInteger)currentDownloadCount { - return _downloadQueue.operationCount; -} - -- (NSInteger)maxConcurrentDownloads { - return _downloadQueue.maxConcurrentOperationCount; -} - -- (void)setOperationClass:(Class)operationClass { - _operationClass = operationClass ?: [SDWebImageDownloaderOperation class]; -} - -- (id )downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock { - __block SDWebImageDownloaderOperation *operation; - __weak __typeof(self)wself = self; - - [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^{ - NSTimeInterval timeoutInterval = wself.downloadTimeout; - if (timeoutInterval == 0.0) { - timeoutInterval = 15.0; - } - - // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; - request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); - request.HTTPShouldUsePipelining = YES; - if (wself.headersFilter) { - request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]); - } - else { - request.allHTTPHeaderFields = wself.HTTPHeaders; - } - operation = [[wself.operationClass alloc] initWithRequest:request - options:options - progress:^(NSInteger receivedSize, NSInteger expectedSize) { - SDWebImageDownloader *sself = wself; - if (!sself) return; - __block NSArray *callbacksForURL; - dispatch_sync(sself.barrierQueue, ^{ - callbacksForURL = [sself.URLCallbacks[url] copy]; - }); - for (NSDictionary *callbacks in callbacksForURL) { - dispatch_async(dispatch_get_main_queue(), ^{ - SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey]; - if (callback) callback(receivedSize, expectedSize); - }); - } - } - completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) { - SDWebImageDownloader *sself = wself; - if (!sself) return; - __block NSArray *callbacksForURL; - dispatch_barrier_sync(sself.barrierQueue, ^{ - callbacksForURL = [sself.URLCallbacks[url] copy]; - if (finished) { - [sself.URLCallbacks removeObjectForKey:url]; - } - }); - for (NSDictionary *callbacks in callbacksForURL) { - SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey]; - if (callback) callback(image, data, error, finished); - } - } - cancelled:^{ - SDWebImageDownloader *sself = wself; - if (!sself) return; - dispatch_barrier_async(sself.barrierQueue, ^{ - [sself.URLCallbacks removeObjectForKey:url]; - }); - }]; - operation.shouldDecompressImages = wself.shouldDecompressImages; - - if (wself.urlCredential) { - operation.credential = wself.urlCredential; - } else if (wself.username && wself.password) { - operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession]; - } - - if (options & SDWebImageDownloaderHighPriority) { - operation.queuePriority = NSOperationQueuePriorityHigh; - } else if (options & SDWebImageDownloaderLowPriority) { - operation.queuePriority = NSOperationQueuePriorityLow; - } - - [wself.downloadQueue addOperation:operation]; - if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { - // Emulate LIFO execution order by systematically adding new operations as last operation's dependency - [wself.lastAddedOperation addDependency:operation]; - wself.lastAddedOperation = operation; - } - }]; - - return operation; -} - -- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback { - // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data. - if (url == nil) { - if (completedBlock != nil) { - completedBlock(nil, nil, nil, NO); - } - return; - } - - dispatch_barrier_sync(self.barrierQueue, ^{ - BOOL first = NO; - if (!self.URLCallbacks[url]) { - self.URLCallbacks[url] = [NSMutableArray new]; - first = YES; - } - - // Handle single download of simultaneous download request for the same URL - NSMutableArray *callbacksForURL = self.URLCallbacks[url]; - NSMutableDictionary *callbacks = [NSMutableDictionary new]; - if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy]; - if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy]; - [callbacksForURL addObject:callbacks]; - self.URLCallbacks[url] = callbacksForURL; - - if (first) { - createCallback(); - } - }); -} - -- (void)setSuspended:(BOOL)suspended { - [self.downloadQueue setSuspended:suspended]; -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h b/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h deleted file mode 100644 index dd48b22..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import -#import "SDWebImageDownloader.h" -#import "SDWebImageOperation.h" - -extern NSString *const SDWebImageDownloadStartNotification; -extern NSString *const SDWebImageDownloadReceiveResponseNotification; -extern NSString *const SDWebImageDownloadStopNotification; -extern NSString *const SDWebImageDownloadFinishNotification; - -@interface SDWebImageDownloaderOperation : NSOperation - -/** - * The request used by the operation's connection. - */ -@property (strong, nonatomic, readonly) NSURLRequest *request; - - -@property (assign, nonatomic) BOOL shouldDecompressImages; - -/** - * Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default. - * - * This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`. - */ -@property (nonatomic, assign) BOOL shouldUseCredentialStorage; - -/** - * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. - * - * This will be overridden by any shared credentials that exist for the username or password of the request URL, if present. - */ -@property (nonatomic, strong) NSURLCredential *credential; - -/** - * The SDWebImageDownloaderOptions for the receiver. - */ -@property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options; - -/** - * The expected size of data. - */ -@property (assign, nonatomic) NSInteger expectedSize; - -/** - * The response returned by the operation's connection. - */ -@property (strong, nonatomic) NSURLResponse *response; - -/** - * Initializes a `SDWebImageDownloaderOperation` object - * - * @see SDWebImageDownloaderOperation - * - * @param request the URL request - * @param options downloader options - * @param progressBlock the block executed when a new chunk of data arrives. - * @note the progress block is executed on a background queue - * @param completedBlock the block executed when the download is done. - * @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue - * @param cancelBlock the block executed if the download (operation) is cancelled - * - * @return the initialized instance - */ -- (id)initWithRequest:(NSURLRequest *)request - options:(SDWebImageDownloaderOptions)options - progress:(SDWebImageDownloaderProgressBlock)progressBlock - completed:(SDWebImageDownloaderCompletedBlock)completedBlock - cancelled:(SDWebImageNoParamsBlock)cancelBlock; - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.m b/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.m deleted file mode 100644 index 5a8bd11..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.m +++ /dev/null @@ -1,466 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "SDWebImageDownloaderOperation.h" -#import "SDWebImageDecoder.h" -#import "UIImage+MultiFormat.h" -#import -#import "SDWebImageManager.h" - -NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification"; -NSString *const SDWebImageDownloadReceiveResponseNotification = @"SDWebImageDownloadReceiveResponseNotification"; -NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification"; -NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinishNotification"; - -@interface SDWebImageDownloaderOperation () - -@property (copy, nonatomic) SDWebImageDownloaderProgressBlock progressBlock; -@property (copy, nonatomic) SDWebImageDownloaderCompletedBlock completedBlock; -@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock; - -@property (assign, nonatomic, getter = isExecuting) BOOL executing; -@property (assign, nonatomic, getter = isFinished) BOOL finished; -@property (strong, nonatomic) NSMutableData *imageData; -@property (strong, nonatomic) NSURLConnection *connection; -@property (strong, atomic) NSThread *thread; - -#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 -@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId; -#endif - -@end - -@implementation SDWebImageDownloaderOperation { - size_t width, height; - UIImageOrientation orientation; - BOOL responseFromCached; -} - -@synthesize executing = _executing; -@synthesize finished = _finished; - -- (id)initWithRequest:(NSURLRequest *)request - options:(SDWebImageDownloaderOptions)options - progress:(SDWebImageDownloaderProgressBlock)progressBlock - completed:(SDWebImageDownloaderCompletedBlock)completedBlock - cancelled:(SDWebImageNoParamsBlock)cancelBlock { - if ((self = [super init])) { - _request = request; - _shouldDecompressImages = YES; - _shouldUseCredentialStorage = YES; - _options = options; - _progressBlock = [progressBlock copy]; - _completedBlock = [completedBlock copy]; - _cancelBlock = [cancelBlock copy]; - _executing = NO; - _finished = NO; - _expectedSize = 0; - responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called - } - return self; -} - -- (void)start { - @synchronized (self) { - if (self.isCancelled) { - self.finished = YES; - [self reset]; - return; - } - -#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 - Class UIApplicationClass = NSClassFromString(@"UIApplication"); - BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)]; - if (hasApplication && [self shouldContinueWhenAppEntersBackground]) { - __weak __typeof__ (self) wself = self; - UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)]; - self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{ - __strong __typeof (wself) sself = wself; - - if (sself) { - [sself cancel]; - - [app endBackgroundTask:sself.backgroundTaskId]; - sself.backgroundTaskId = UIBackgroundTaskInvalid; - } - }]; - } -#endif - - self.executing = YES; - self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; - self.thread = [NSThread currentThread]; - } - - [self.connection start]; - - if (self.connection) { - if (self.progressBlock) { - self.progressBlock(0, NSURLResponseUnknownLength); - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self]; - }); - - if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) { - // Make sure to run the runloop in our background thread so it can process downloaded data - // Note: we use a timeout to work around an issue with NSURLConnection cancel under iOS 5 - // not waking up the runloop, leading to dead threads (see https://github.com/rs/SDWebImage/issues/466) - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false); - } - else { - CFRunLoopRun(); - } - - if (!self.isFinished) { - [self.connection cancel]; - [self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey : self.request.URL}]]; - } - } - else { - if (self.completedBlock) { - self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES); - } - } - -#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 - Class UIApplicationClass = NSClassFromString(@"UIApplication"); - if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) { - return; - } - if (self.backgroundTaskId != UIBackgroundTaskInvalid) { - UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)]; - [app endBackgroundTask:self.backgroundTaskId]; - self.backgroundTaskId = UIBackgroundTaskInvalid; - } -#endif -} - -- (void)cancel { - @synchronized (self) { - if (self.thread) { - [self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO]; - } - else { - [self cancelInternal]; - } - } -} - -- (void)cancelInternalAndStop { - if (self.isFinished) return; - [self cancelInternal]; - CFRunLoopStop(CFRunLoopGetCurrent()); -} - -- (void)cancelInternal { - if (self.isFinished) return; - [super cancel]; - if (self.cancelBlock) self.cancelBlock(); - - if (self.connection) { - [self.connection cancel]; - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; - }); - - // As we cancelled the connection, its callback won't be called and thus won't - // maintain the isFinished and isExecuting flags. - if (self.isExecuting) self.executing = NO; - if (!self.isFinished) self.finished = YES; - } - - [self reset]; -} - -- (void)done { - self.finished = YES; - self.executing = NO; - [self reset]; -} - -- (void)reset { - self.cancelBlock = nil; - self.completedBlock = nil; - self.progressBlock = nil; - self.connection = nil; - self.imageData = nil; - self.thread = nil; -} - -- (void)setFinished:(BOOL)finished { - [self willChangeValueForKey:@"isFinished"]; - _finished = finished; - [self didChangeValueForKey:@"isFinished"]; -} - -- (void)setExecuting:(BOOL)executing { - [self willChangeValueForKey:@"isExecuting"]; - _executing = executing; - [self didChangeValueForKey:@"isExecuting"]; -} - -- (BOOL)isConcurrent { - return YES; -} - -#pragma mark NSURLConnection (delegate) - -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { - - //'304 Not Modified' is an exceptional one - if (![response respondsToSelector:@selector(statusCode)] || ([((NSHTTPURLResponse *)response) statusCode] < 400 && [((NSHTTPURLResponse *)response) statusCode] != 304)) { - NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0; - self.expectedSize = expected; - if (self.progressBlock) { - self.progressBlock(0, expected); - } - - self.imageData = [[NSMutableData alloc] initWithCapacity:expected]; - self.response = response; - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadReceiveResponseNotification object:self]; - }); - } - else { - NSUInteger code = [((NSHTTPURLResponse *)response) statusCode]; - - //This is the case when server returns '304 Not Modified'. It means that remote image is not changed. - //In case of 304 we need just cancel the operation and return cached image from the cache. - if (code == 304) { - [self cancelInternal]; - } else { - [self.connection cancel]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; - }); - - if (self.completedBlock) { - self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], YES); - } - CFRunLoopStop(CFRunLoopGetCurrent()); - [self done]; - } -} - -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { - [self.imageData appendData:data]; - - if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0 && self.completedBlock) { - // The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/ - // Thanks to the author @Nyx0uf - - // Get the total bytes downloaded - const NSInteger totalSize = self.imageData.length; - - // Update the data source, we must pass ALL the data, not just the new bytes - CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)self.imageData, NULL); - - if (width + height == 0) { - CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); - if (properties) { - NSInteger orientationValue = -1; - CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight); - if (val) CFNumberGetValue(val, kCFNumberLongType, &height); - val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); - if (val) CFNumberGetValue(val, kCFNumberLongType, &width); - val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); - if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue); - CFRelease(properties); - - // When we draw to Core Graphics, we lose orientation information, - // which means the image below born of initWithCGIImage will be - // oriented incorrectly sometimes. (Unlike the image born of initWithData - // in connectionDidFinishLoading.) So save it here and pass it on later. - orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)]; - } - - } - - if (width + height > 0 && totalSize < self.expectedSize) { - // Create the image - CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); - -#ifdef TARGET_OS_IPHONE - // Workaround for iOS anamorphic image - if (partialImageRef) { - const size_t partialHeight = CGImageGetHeight(partialImageRef); - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); - CGColorSpaceRelease(colorSpace); - if (bmContext) { - CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef); - CGImageRelease(partialImageRef); - partialImageRef = CGBitmapContextCreateImage(bmContext); - CGContextRelease(bmContext); - } - else { - CGImageRelease(partialImageRef); - partialImageRef = nil; - } - } -#endif - - if (partialImageRef) { - UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:orientation]; - NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; - UIImage *scaledImage = [self scaledImageForKey:key image:image]; - if (self.shouldDecompressImages) { - image = [UIImage decodedImageWithImage:scaledImage]; - } - else { - image = scaledImage; - } - CGImageRelease(partialImageRef); - dispatch_main_sync_safe(^{ - if (self.completedBlock) { - self.completedBlock(image, nil, nil, NO); - } - }); - } - } - - CFRelease(imageSource); - } - - if (self.progressBlock) { - self.progressBlock(self.imageData.length, self.expectedSize); - } -} - -+ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value { - switch (value) { - case 1: - return UIImageOrientationUp; - case 3: - return UIImageOrientationDown; - case 8: - return UIImageOrientationLeft; - case 6: - return UIImageOrientationRight; - case 2: - return UIImageOrientationUpMirrored; - case 4: - return UIImageOrientationDownMirrored; - case 5: - return UIImageOrientationLeftMirrored; - case 7: - return UIImageOrientationRightMirrored; - default: - return UIImageOrientationUp; - } -} - -- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image { - return SDScaledImageForKey(key, image); -} - -- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection { - SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock; - @synchronized(self) { - CFRunLoopStop(CFRunLoopGetCurrent()); - self.thread = nil; - self.connection = nil; - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:self]; - }); - } - - if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) { - responseFromCached = NO; - } - - if (completionBlock) { - if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) { - completionBlock(nil, nil, nil, YES); - } else if (self.imageData) { - UIImage *image = [UIImage sd_imageWithData:self.imageData]; - NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; - image = [self scaledImageForKey:key image:image]; - - // Do not force decoding animated GIFs - if (!image.images) { - if (self.shouldDecompressImages) { - image = [UIImage decodedImageWithImage:image]; - } - } - if (CGSizeEqualToSize(image.size, CGSizeZero)) { - completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}], YES); - } - else { - completionBlock(image, self.imageData, nil, YES); - } - } else { - completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}], YES); - } - } - self.completionBlock = nil; - [self done]; -} - -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { - @synchronized(self) { - CFRunLoopStop(CFRunLoopGetCurrent()); - self.thread = nil; - self.connection = nil; - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; - }); - } - - if (self.completedBlock) { - self.completedBlock(nil, nil, error, YES); - } - self.completionBlock = nil; - [self done]; -} - -- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { - responseFromCached = NO; // If this method is called, it means the response wasn't read from cache - if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) { - // Prevents caching of responses - return nil; - } - else { - return cachedResponse; - } -} - -- (BOOL)shouldContinueWhenAppEntersBackground { - return self.options & SDWebImageDownloaderContinueInBackground; -} - -- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection { - return self.shouldUseCredentialStorage; -} - -- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{ - if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { - if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates) && - [challenge.sender respondsToSelector:@selector(performDefaultHandlingForAuthenticationChallenge:)]) { - [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; - } else { - NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; - [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; - } - } else { - if ([challenge previousFailureCount] == 0) { - if (self.credential) { - [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge]; - } else { - [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; - } - } else { - [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; - } - } -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageManager.h b/Pods/SDWebImage/SDWebImage/SDWebImageManager.h deleted file mode 100644 index 18e578a..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageManager.h +++ /dev/null @@ -1,299 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "SDWebImageCompat.h" -#import "SDWebImageOperation.h" -#import "SDWebImageDownloader.h" -#import "SDImageCache.h" - -typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) { - /** - * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying. - * This flag disable this blacklisting. - */ - SDWebImageRetryFailed = 1 << 0, - - /** - * By default, image downloads are started during UI interactions, this flags disable this feature, - * leading to delayed download on UIScrollView deceleration for instance. - */ - SDWebImageLowPriority = 1 << 1, - - /** - * This flag disables on-disk caching - */ - SDWebImageCacheMemoryOnly = 1 << 2, - - /** - * This flag enables progressive download, the image is displayed progressively during download as a browser would do. - * By default, the image is only displayed once completely downloaded. - */ - SDWebImageProgressiveDownload = 1 << 3, - - /** - * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed. - * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation. - * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics. - * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image. - * - * Use this flag only if you can't make your URLs static with embedded cache busting parameter. - */ - SDWebImageRefreshCached = 1 << 4, - - /** - * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for - * extra time in background to let the request finish. If the background task expires the operation will be cancelled. - */ - SDWebImageContinueInBackground = 1 << 5, - - /** - * Handles cookies stored in NSHTTPCookieStore by setting - * NSMutableURLRequest.HTTPShouldHandleCookies = YES; - */ - SDWebImageHandleCookies = 1 << 6, - - /** - * Enable to allow untrusted SSL certificates. - * Useful for testing purposes. Use with caution in production. - */ - SDWebImageAllowInvalidSSLCertificates = 1 << 7, - - /** - * By default, image are loaded in the order they were queued. This flag move them to - * the front of the queue and is loaded immediately instead of waiting for the current queue to be loaded (which - * could take a while). - */ - SDWebImageHighPriority = 1 << 8, - - /** - * By default, placeholder images are loaded while the image is loading. This flag will delay the loading - * of the placeholder image until after the image has finished loading. - */ - SDWebImageDelayPlaceholder = 1 << 9, - - /** - * We usually don't call transformDownloadedImage delegate method on animated images, - * as most transformation code would mangle it. - * Use this flag to transform them anyway. - */ - SDWebImageTransformAnimatedImage = 1 << 10, - - /** - * By default, image is added to the imageView after download. But in some cases, we want to - * have the hand before setting the image (apply a filter or add it with cross-fade animation for instance) - * Use this flag if you want to manually set the image in the completion when success - */ - SDWebImageAvoidAutoSetImage = 1 << 11 -}; - -typedef void(^SDWebImageCompletionBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL); - -typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL); - -typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url); - - -@class SDWebImageManager; - -@protocol SDWebImageManagerDelegate - -@optional - -/** - * Controls which image should be downloaded when the image is not found in the cache. - * - * @param imageManager The current `SDWebImageManager` - * @param imageURL The url of the image to be downloaded - * - * @return Return NO to prevent the downloading of the image on cache misses. If not implemented, YES is implied. - */ -- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL; - -/** - * Allows to transform the image immediately after it has been downloaded and just before to cache it on disk and memory. - * NOTE: This method is called from a global queue in order to not to block the main thread. - * - * @param imageManager The current `SDWebImageManager` - * @param image The image to transform - * @param imageURL The url of the image to transform - * - * @return The transformed image object. - */ -- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL; - -@end - -/** - * The SDWebImageManager is the class behind the UIImageView+WebCache category and likes. - * It ties the asynchronous downloader (SDWebImageDownloader) with the image cache store (SDImageCache). - * You can use this class directly to benefit from web image downloading with caching in another context than - * a UIView. - * - * Here is a simple example of how to use SDWebImageManager: - * - * @code - -SDWebImageManager *manager = [SDWebImageManager sharedManager]; -[manager downloadImageWithURL:imageURL - options:0 - progress:nil - completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { - if (image) { - // do something with image - } - }]; - - * @endcode - */ -@interface SDWebImageManager : NSObject - -@property (weak, nonatomic) id delegate; - -@property (strong, nonatomic, readonly) SDImageCache *imageCache; -@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader; - -/** - * The cache filter is a block used each time SDWebImageManager need to convert an URL into a cache key. This can - * be used to remove dynamic part of an image URL. - * - * The following example sets a filter in the application delegate that will remove any query-string from the - * URL before to use it as a cache key: - * - * @code - -[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) { - url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path]; - return [url absoluteString]; -}]; - - * @endcode - */ -@property (nonatomic, copy) SDWebImageCacheKeyFilterBlock cacheKeyFilter; - -/** - * Returns global SDWebImageManager instance. - * - * @return SDWebImageManager shared instance - */ -+ (SDWebImageManager *)sharedManager; - -/** - * Downloads the image at the given URL if not present in cache or return the cached version otherwise. - * - * @param url The URL to the image - * @param options A mask to specify options to use for this request - * @param progressBlock A block called while image is downloading - * @param completedBlock A block called when operation has been completed. - * - * This parameter is required. - * - * This block has no return value and takes the requested UIImage as first parameter. - * In case of error the image parameter is nil and the second parameter may contain an NSError. - * - * The third parameter is an `SDImageCacheType` enum indicating if the image was retrieved from the local cache - * or from the memory cache or from the network. - * - * The last parameter is set to NO when the SDWebImageProgressiveDownload option is used and the image is - * downloading. This block is thus called repeatedly with a partial image. When image is fully downloaded, the - * block is called a last time with the full image and the last parameter set to YES. - * - * @return Returns an NSObject conforming to SDWebImageOperation. Should be an instance of SDWebImageDownloaderOperation - */ -- (id )downloadImageWithURL:(NSURL *)url - options:(SDWebImageOptions)options - progress:(SDWebImageDownloaderProgressBlock)progressBlock - completed:(SDWebImageCompletionWithFinishedBlock)completedBlock; - -/** - * Saves image to cache for given URL - * - * @param image The image to cache - * @param url The URL to the image - * - */ - -- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url; - -/** - * Cancel all current operations - */ -- (void)cancelAll; - -/** - * Check one or more operations running - */ -- (BOOL)isRunning; - -/** - * Check if image has already been cached - * - * @param url image url - * - * @return if the image was already cached - */ -- (BOOL)cachedImageExistsForURL:(NSURL *)url; - -/** - * Check if image has already been cached on disk only - * - * @param url image url - * - * @return if the image was already cached (disk only) - */ -- (BOOL)diskImageExistsForURL:(NSURL *)url; - -/** - * Async check if image has already been cached - * - * @param url image url - * @param completionBlock the block to be executed when the check is finished - * - * @note the completion block is always executed on the main queue - */ -- (void)cachedImageExistsForURL:(NSURL *)url - completion:(SDWebImageCheckCacheCompletionBlock)completionBlock; - -/** - * Async check if image has already been cached on disk only - * - * @param url image url - * @param completionBlock the block to be executed when the check is finished - * - * @note the completion block is always executed on the main queue - */ -- (void)diskImageExistsForURL:(NSURL *)url - completion:(SDWebImageCheckCacheCompletionBlock)completionBlock; - - -/** - *Return the cache key for a given URL - */ -- (NSString *)cacheKeyForURL:(NSURL *)url; - -@end - - -#pragma mark - Deprecated - -typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType) __deprecated_msg("Block type deprecated. Use `SDWebImageCompletionBlock`"); -typedef void(^SDWebImageCompletedWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) __deprecated_msg("Block type deprecated. Use `SDWebImageCompletionWithFinishedBlock`"); - - -@interface SDWebImageManager (Deprecated) - -/** - * Downloads the image at the given URL if not present in cache or return the cached version otherwise. - * - * @deprecated This method has been deprecated. Use `downloadImageWithURL:options:progress:completed:` - */ -- (id )downloadWithURL:(NSURL *)url - options:(SDWebImageOptions)options - progress:(SDWebImageDownloaderProgressBlock)progressBlock - completed:(SDWebImageCompletedWithFinishedBlock)completedBlock __deprecated_msg("Method deprecated. Use `downloadImageWithURL:options:progress:completed:`"); - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageManager.m b/Pods/SDWebImage/SDWebImage/SDWebImageManager.m deleted file mode 100644 index fdb68ba..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageManager.m +++ /dev/null @@ -1,370 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "SDWebImageManager.h" -#import - -@interface SDWebImageCombinedOperation : NSObject - -@property (assign, nonatomic, getter = isCancelled) BOOL cancelled; -@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock; -@property (strong, nonatomic) NSOperation *cacheOperation; - -@end - -@interface SDWebImageManager () - -@property (strong, nonatomic, readwrite) SDImageCache *imageCache; -@property (strong, nonatomic, readwrite) SDWebImageDownloader *imageDownloader; -@property (strong, nonatomic) NSMutableSet *failedURLs; -@property (strong, nonatomic) NSMutableArray *runningOperations; - -@end - -@implementation SDWebImageManager - -+ (id)sharedManager { - static dispatch_once_t once; - static id instance; - dispatch_once(&once, ^{ - instance = [self new]; - }); - return instance; -} - -- (id)init { - if ((self = [super init])) { - _imageCache = [self createCache]; - _imageDownloader = [SDWebImageDownloader sharedDownloader]; - _failedURLs = [NSMutableSet new]; - _runningOperations = [NSMutableArray new]; - } - return self; -} - -- (SDImageCache *)createCache { - return [SDImageCache sharedImageCache]; -} - -- (NSString *)cacheKeyForURL:(NSURL *)url { - if (self.cacheKeyFilter) { - return self.cacheKeyFilter(url); - } - else { - return [url absoluteString]; - } -} - -- (BOOL)cachedImageExistsForURL:(NSURL *)url { - NSString *key = [self cacheKeyForURL:url]; - if ([self.imageCache imageFromMemoryCacheForKey:key] != nil) return YES; - return [self.imageCache diskImageExistsWithKey:key]; -} - -- (BOOL)diskImageExistsForURL:(NSURL *)url { - NSString *key = [self cacheKeyForURL:url]; - return [self.imageCache diskImageExistsWithKey:key]; -} - -- (void)cachedImageExistsForURL:(NSURL *)url - completion:(SDWebImageCheckCacheCompletionBlock)completionBlock { - NSString *key = [self cacheKeyForURL:url]; - - BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil); - - if (isInMemoryCache) { - // making sure we call the completion block on the main queue - dispatch_async(dispatch_get_main_queue(), ^{ - if (completionBlock) { - completionBlock(YES); - } - }); - return; - } - - [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { - // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch - if (completionBlock) { - completionBlock(isInDiskCache); - } - }]; -} - -- (void)diskImageExistsForURL:(NSURL *)url - completion:(SDWebImageCheckCacheCompletionBlock)completionBlock { - NSString *key = [self cacheKeyForURL:url]; - - [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { - // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch - if (completionBlock) { - completionBlock(isInDiskCache); - } - }]; -} - -- (id )downloadImageWithURL:(NSURL *)url - options:(SDWebImageOptions)options - progress:(SDWebImageDownloaderProgressBlock)progressBlock - completed:(SDWebImageCompletionWithFinishedBlock)completedBlock { - // Invoking this method without a completedBlock is pointless - NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead"); - - // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't - // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString. - if ([url isKindOfClass:NSString.class]) { - url = [NSURL URLWithString:(NSString *)url]; - } - - // Prevents app crashing on argument type error like sending NSNull instead of NSURL - if (![url isKindOfClass:NSURL.class]) { - url = nil; - } - - __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new]; - __weak SDWebImageCombinedOperation *weakOperation = operation; - - BOOL isFailedUrl = NO; - @synchronized (self.failedURLs) { - isFailedUrl = [self.failedURLs containsObject:url]; - } - - if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { - dispatch_main_sync_safe(^{ - NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]; - completedBlock(nil, error, SDImageCacheTypeNone, YES, url); - }); - return operation; - } - - @synchronized (self.runningOperations) { - [self.runningOperations addObject:operation]; - } - NSString *key = [self cacheKeyForURL:url]; - - operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) { - if (operation.isCancelled) { - @synchronized (self.runningOperations) { - [self.runningOperations removeObject:operation]; - } - - return; - } - - if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { - if (image && options & SDWebImageRefreshCached) { - dispatch_main_sync_safe(^{ - // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image - // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server. - completedBlock(image, nil, cacheType, YES, url); - }); - } - - // download if no image or requested to refresh anyway, and download allowed by delegate - SDWebImageDownloaderOptions downloaderOptions = 0; - if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; - if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; - if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache; - if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground; - if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies; - if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates; - if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority; - if (image && options & SDWebImageRefreshCached) { - // force progressive off if image already cached but forced refreshing - downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload; - // ignore image read from NSURLCache if image if cached but force refreshing - downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; - } - id subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) { - __strong __typeof(weakOperation) strongOperation = weakOperation; - if (!strongOperation || strongOperation.isCancelled) { - // Do nothing if the operation was cancelled - // See #699 for more details - // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data - } - else if (error) { - dispatch_main_sync_safe(^{ - if (strongOperation && !strongOperation.isCancelled) { - completedBlock(nil, error, SDImageCacheTypeNone, finished, url); - } - }); - - if ( error.code != NSURLErrorNotConnectedToInternet - && error.code != NSURLErrorCancelled - && error.code != NSURLErrorTimedOut - && error.code != NSURLErrorInternationalRoamingOff - && error.code != NSURLErrorDataNotAllowed - && error.code != NSURLErrorCannotFindHost - && error.code != NSURLErrorCannotConnectToHost) { - @synchronized (self.failedURLs) { - [self.failedURLs addObject:url]; - } - } - } - else { - if ((options & SDWebImageRetryFailed)) { - @synchronized (self.failedURLs) { - [self.failedURLs removeObject:url]; - } - } - - BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); - - if (options & SDWebImageRefreshCached && image && !downloadedImage) { - // Image refresh hit the NSURLCache cache, do not call the completion block - } - else if (downloadedImage && (!downloadedImage.images && (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ - UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; - - if (transformedImage && finished) { - BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage]; - [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk]; - } - - dispatch_main_sync_safe(^{ - if (strongOperation && !strongOperation.isCancelled) { - completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url); - } - }); - }); - } - else { - if (downloadedImage && finished) { - [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk]; - } - - dispatch_main_sync_safe(^{ - if (strongOperation && !strongOperation.isCancelled) { - completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url); - } - }); - } - } - - if (finished) { - @synchronized (self.runningOperations) { - if (strongOperation) { - [self.runningOperations removeObject:strongOperation]; - } - } - } - }]; - operation.cancelBlock = ^{ - [subOperation cancel]; - - @synchronized (self.runningOperations) { - __strong __typeof(weakOperation) strongOperation = weakOperation; - if (strongOperation) { - [self.runningOperations removeObject:strongOperation]; - } - } - }; - } - else if (image) { - dispatch_main_sync_safe(^{ - __strong __typeof(weakOperation) strongOperation = weakOperation; - if (strongOperation && !strongOperation.isCancelled) { - completedBlock(image, nil, cacheType, YES, url); - } - }); - @synchronized (self.runningOperations) { - [self.runningOperations removeObject:operation]; - } - } - else { - // Image not in cache and download disallowed by delegate - dispatch_main_sync_safe(^{ - __strong __typeof(weakOperation) strongOperation = weakOperation; - if (strongOperation && !weakOperation.isCancelled) { - completedBlock(nil, nil, SDImageCacheTypeNone, YES, url); - } - }); - @synchronized (self.runningOperations) { - [self.runningOperations removeObject:operation]; - } - } - }]; - - return operation; -} - -- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url { - if (image && url) { - NSString *key = [self cacheKeyForURL:url]; - [self.imageCache storeImage:image forKey:key toDisk:YES]; - } -} - -- (void)cancelAll { - @synchronized (self.runningOperations) { - NSArray *copiedOperations = [self.runningOperations copy]; - [copiedOperations makeObjectsPerformSelector:@selector(cancel)]; - [self.runningOperations removeObjectsInArray:copiedOperations]; - } -} - -- (BOOL)isRunning { - BOOL isRunning = NO; - @synchronized(self.runningOperations) { - isRunning = (self.runningOperations.count > 0); - } - return isRunning; -} - -@end - - -@implementation SDWebImageCombinedOperation - -- (void)setCancelBlock:(SDWebImageNoParamsBlock)cancelBlock { - // check if the operation is already cancelled, then we just call the cancelBlock - if (self.isCancelled) { - if (cancelBlock) { - cancelBlock(); - } - _cancelBlock = nil; // don't forget to nil the cancelBlock, otherwise we will get crashes - } else { - _cancelBlock = [cancelBlock copy]; - } -} - -- (void)cancel { - self.cancelled = YES; - if (self.cacheOperation) { - [self.cacheOperation cancel]; - self.cacheOperation = nil; - } - if (self.cancelBlock) { - self.cancelBlock(); - - // TODO: this is a temporary fix to #809. - // Until we can figure the exact cause of the crash, going with the ivar instead of the setter -// self.cancelBlock = nil; - _cancelBlock = nil; - } -} - -@end - - -@implementation SDWebImageManager (Deprecated) - -// deprecated method, uses the non deprecated method -// adapter for the completion block -- (id )downloadWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedWithFinishedBlock)completedBlock { - return [self downloadImageWithURL:url - options:options - progress:progressBlock - completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType, finished); - } - }]; -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageOperation.h b/Pods/SDWebImage/SDWebImage/SDWebImageOperation.h deleted file mode 100644 index 71094ee..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImageOperation.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import - -@protocol SDWebImageOperation - -- (void)cancel; - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.h b/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.h deleted file mode 100644 index 6c31b15..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import -#import "SDWebImageManager.h" - -@class SDWebImagePrefetcher; - -@protocol SDWebImagePrefetcherDelegate - -@optional - -/** - * Called when an image was prefetched. - * - * @param imagePrefetcher The current image prefetcher - * @param imageURL The image url that was prefetched - * @param finishedCount The total number of images that were prefetched (successful or not) - * @param totalCount The total number of images that were to be prefetched - */ -- (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount; - -/** - * Called when all images are prefetched. - * @param imagePrefetcher The current image prefetcher - * @param totalCount The total number of images that were prefetched (whether successful or not) - * @param skippedCount The total number of images that were skipped - */ -- (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount; - -@end - -typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls); -typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls); - -/** - * Prefetch some URLs in the cache for future use. Images are downloaded in low priority. - */ -@interface SDWebImagePrefetcher : NSObject - -/** - * The web image manager - */ -@property (strong, nonatomic, readonly) SDWebImageManager *manager; - -/** - * Maximum number of URLs to prefetch at the same time. Defaults to 3. - */ -@property (nonatomic, assign) NSUInteger maxConcurrentDownloads; - -/** - * SDWebImageOptions for prefetcher. Defaults to SDWebImageLowPriority. - */ -@property (nonatomic, assign) SDWebImageOptions options; - -/** - * Queue options for Prefetcher. Defaults to Main Queue. - */ -@property (nonatomic, assign) dispatch_queue_t prefetcherQueue; - -@property (weak, nonatomic) id delegate; - -/** - * Return the global image prefetcher instance. - */ -+ (SDWebImagePrefetcher *)sharedImagePrefetcher; - -/** - * Allows you to instantiate a prefetcher with any arbitrary image manager. - */ -- (id)initWithImageManager:(SDWebImageManager *)manager; - -/** - * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, - * currently one image is downloaded at a time, - * and skips images for failed downloads and proceed to the next image in the list - * - * @param urls list of URLs to prefetch - */ -- (void)prefetchURLs:(NSArray *)urls; - -/** - * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, - * currently one image is downloaded at a time, - * and skips images for failed downloads and proceed to the next image in the list - * - * @param urls list of URLs to prefetch - * @param progressBlock block to be called when progress updates; - * first parameter is the number of completed (successful or not) requests, - * second parameter is the total number of images originally requested to be prefetched - * @param completionBlock block to be called when prefetching is completed - * first param is the number of completed (successful or not) requests, - * second parameter is the number of skipped requests - */ -- (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(SDWebImagePrefetcherCompletionBlock)completionBlock; - -/** - * Remove and cancel queued list - */ -- (void)cancelPrefetching; - - -@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.m b/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.m deleted file mode 100644 index f518d44..0000000 --- a/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.m +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "SDWebImagePrefetcher.h" - -@interface SDWebImagePrefetcher () - -@property (strong, nonatomic) SDWebImageManager *manager; -@property (strong, nonatomic) NSArray *prefetchURLs; -@property (assign, nonatomic) NSUInteger requestedCount; -@property (assign, nonatomic) NSUInteger skippedCount; -@property (assign, nonatomic) NSUInteger finishedCount; -@property (assign, nonatomic) NSTimeInterval startedTime; -@property (copy, nonatomic) SDWebImagePrefetcherCompletionBlock completionBlock; -@property (copy, nonatomic) SDWebImagePrefetcherProgressBlock progressBlock; - -@end - -@implementation SDWebImagePrefetcher - -+ (SDWebImagePrefetcher *)sharedImagePrefetcher { - static dispatch_once_t once; - static id instance; - dispatch_once(&once, ^{ - instance = [self new]; - }); - return instance; -} - -- (id)init { - return [self initWithImageManager:[SDWebImageManager new]]; -} - -- (id)initWithImageManager:(SDWebImageManager *)manager { - if ((self = [super init])) { - _manager = manager; - _options = SDWebImageLowPriority; - _prefetcherQueue = dispatch_get_main_queue(); - self.maxConcurrentDownloads = 3; - } - return self; -} - -- (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads { - self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads; -} - -- (NSUInteger)maxConcurrentDownloads { - return self.manager.imageDownloader.maxConcurrentDownloads; -} - -- (void)startPrefetchingAtIndex:(NSUInteger)index { - if (index >= self.prefetchURLs.count) return; - self.requestedCount++; - [self.manager downloadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { - if (!finished) return; - self.finishedCount++; - - if (image) { - if (self.progressBlock) { - self.progressBlock(self.finishedCount,[self.prefetchURLs count]); - } - } - else { - if (self.progressBlock) { - self.progressBlock(self.finishedCount,[self.prefetchURLs count]); - } - // Add last failed - self.skippedCount++; - } - if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) { - [self.delegate imagePrefetcher:self - didPrefetchURL:self.prefetchURLs[index] - finishedCount:self.finishedCount - totalCount:self.prefetchURLs.count - ]; - } - if (self.prefetchURLs.count > self.requestedCount) { - dispatch_async(self.prefetcherQueue, ^{ - [self startPrefetchingAtIndex:self.requestedCount]; - }); - } else if (self.finishedCount == self.requestedCount) { - [self reportStatus]; - if (self.completionBlock) { - self.completionBlock(self.finishedCount, self.skippedCount); - self.completionBlock = nil; - } - self.progressBlock = nil; - } - }]; -} - -- (void)reportStatus { - NSUInteger total = [self.prefetchURLs count]; - if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) { - [self.delegate imagePrefetcher:self - didFinishWithTotalCount:(total - self.skippedCount) - skippedCount:self.skippedCount - ]; - } -} - -- (void)prefetchURLs:(NSArray *)urls { - [self prefetchURLs:urls progress:nil completed:nil]; -} - -- (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(SDWebImagePrefetcherCompletionBlock)completionBlock { - [self cancelPrefetching]; // Prevent duplicate prefetch request - self.startedTime = CFAbsoluteTimeGetCurrent(); - self.prefetchURLs = urls; - self.completionBlock = completionBlock; - self.progressBlock = progressBlock; - - if (urls.count == 0) { - if (completionBlock) { - completionBlock(0,0); - } - } else { - // Starts prefetching from the very first image on the list with the max allowed concurrency - NSUInteger listCount = self.prefetchURLs.count; - for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) { - [self startPrefetchingAtIndex:i]; - } - } -} - -- (void)cancelPrefetching { - self.prefetchURLs = nil; - self.skippedCount = 0; - self.requestedCount = 0; - self.finishedCount = 0; - [self.manager cancelAll]; -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIButton+WebCache.h b/Pods/SDWebImage/SDWebImage/UIButton+WebCache.h deleted file mode 100644 index ecf5ced..0000000 --- a/Pods/SDWebImage/SDWebImage/UIButton+WebCache.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "SDWebImageCompat.h" -#import "SDWebImageManager.h" - -/** - * Integrates SDWebImage async downloading and caching of remote images with UIButtonView. - */ -@interface UIButton (WebCache) - -/** - * Get the current image URL. - */ -- (NSURL *)sd_currentImageURL; - -/** - * Get the image URL for a control state. - * - * @param state Which state you want to know the URL for. The values are described in UIControlState. - */ -- (NSURL *)sd_imageURLForState:(UIControlState)state; - -/** - * Set the imageView `image` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - */ -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state; - -/** - * Set the imageView `image` with an `url` and a placeholder. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - * @param placeholder The image to be set initially, until the image request finishes. - * @see sd_setImageWithURL:placeholderImage:options: - */ -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - */ -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; - -/** - * Set the imageView `image` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - * @param placeholder The image to be set initially, until the image request finishes. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the backgroundImageView `image` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - */ -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state; - -/** - * Set the backgroundImageView `image` with an `url` and a placeholder. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - * @param placeholder The image to be set initially, until the image request finishes. - * @see sd_setImageWithURL:placeholderImage:options: - */ -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder; - -/** - * Set the backgroundImageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - */ -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; - -/** - * Set the backgroundImageView `image` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the backgroundImageView `image` with an `url`, placeholder. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param state The state that uses the specified title. The values are described in UIControlState. - * @param placeholder The image to be set initially, until the image request finishes. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the backgroundImageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Cancel the current image download - */ -- (void)sd_cancelImageLoadForState:(UIControlState)state; - -/** - * Cancel the current backgroundImage download - */ -- (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state; - -@end - - -@interface UIButton (WebCacheDeprecated) - -- (NSURL *)currentImageURL __deprecated_msg("Use `sd_currentImageURL`"); -- (NSURL *)imageURLForState:(UIControlState)state __deprecated_msg("Use `sd_imageURLForState:`"); - -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:`"); -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:`"); -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:options:`"); - -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:completed:`"); -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:completed:`"); -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:options:completed:`"); - -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:`"); -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:`"); -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:options:`"); - -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:completed:`"); -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:completed:`"); -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:options:completed:`"); - -- (void)cancelCurrentImageLoad __deprecated_msg("Use `sd_cancelImageLoadForState:`"); -- (void)cancelBackgroundImageLoadForState:(UIControlState)state __deprecated_msg("Use `sd_cancelBackgroundImageLoadForState:`"); - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIButton+WebCache.m b/Pods/SDWebImage/SDWebImage/UIButton+WebCache.m deleted file mode 100644 index ce2175d..0000000 --- a/Pods/SDWebImage/SDWebImage/UIButton+WebCache.m +++ /dev/null @@ -1,270 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "UIButton+WebCache.h" -#import "objc/runtime.h" -#import "UIView+WebCacheOperation.h" - -static char imageURLStorageKey; - -@implementation UIButton (WebCache) - -- (NSURL *)sd_currentImageURL { - NSURL *url = self.imageURLStorage[@(self.state)]; - - if (!url) { - url = self.imageURLStorage[@(UIControlStateNormal)]; - } - - return url; -} - -- (NSURL *)sd_imageURLForState:(UIControlState)state { - return self.imageURLStorage[@(state)]; -} - -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state { - [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; -} - -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { - [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; -} - -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { - [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; -} - -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { - - [self setImage:placeholder forState:state]; - [self sd_cancelImageLoadForState:state]; - - if (!url) { - [self.imageURLStorage removeObjectForKey:@(state)]; - - dispatch_main_async_safe(^{ - if (completedBlock) { - NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; - completedBlock(nil, error, SDImageCacheTypeNone, url); - } - }); - - return; - } - - self.imageURLStorage[@(state)] = url; - - __weak __typeof(self)wself = self; - id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { - if (!wself) return; - dispatch_main_sync_safe(^{ - __strong UIButton *sself = wself; - if (!sself) return; - if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) - { - completedBlock(image, error, cacheType, url); - return; - } - else if (image) { - [sself setImage:image forState:state]; - } - if (completedBlock && finished) { - completedBlock(image, error, cacheType, url); - } - }); - }]; - [self sd_setImageLoadOperation:operation forState:state]; -} - -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; -} - -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; -} - -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; -} - -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock]; -} - -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock]; -} - -- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_cancelBackgroundImageLoadForState:state]; - - [self setBackgroundImage:placeholder forState:state]; - - if (url) { - __weak __typeof(self)wself = self; - id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { - if (!wself) return; - dispatch_main_sync_safe(^{ - __strong UIButton *sself = wself; - if (!sself) return; - if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) - { - completedBlock(image, error, cacheType, url); - return; - } - else if (image) { - [sself setBackgroundImage:image forState:state]; - } - if (completedBlock && finished) { - completedBlock(image, error, cacheType, url); - } - }); - }]; - [self sd_setBackgroundImageLoadOperation:operation forState:state]; - } else { - dispatch_main_async_safe(^{ - NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; - if (completedBlock) { - completedBlock(nil, error, SDImageCacheTypeNone, url); - } - }); - } -} - -- (void)sd_setImageLoadOperation:(id)operation forState:(UIControlState)state { - [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]]; -} - -- (void)sd_cancelImageLoadForState:(UIControlState)state { - [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]]; -} - -- (void)sd_setBackgroundImageLoadOperation:(id)operation forState:(UIControlState)state { - [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]]; -} - -- (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state { - [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]]; -} - -- (NSMutableDictionary *)imageURLStorage { - NSMutableDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey); - if (!storage) - { - storage = [NSMutableDictionary dictionary]; - objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - - return storage; -} - -@end - - -@implementation UIButton (WebCacheDeprecated) - -- (NSURL *)currentImageURL { - return [self sd_currentImageURL]; -} - -- (NSURL *)imageURLForState:(UIControlState)state { - return [self sd_imageURLForState:state]; -} - -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state { - [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; -} - -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { - [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; -} - -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { - [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; -} - -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; -} - -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; -} - -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; -} - -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)cancelCurrentImageLoad { - // in a backwards compatible manner, cancel for current state - [self sd_cancelImageLoadForState:self.state]; -} - -- (void)cancelBackgroundImageLoadForState:(UIControlState)state { - [self sd_cancelBackgroundImageLoadForState:state]; -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIImage+GIF.h b/Pods/SDWebImage/SDWebImage/UIImage+GIF.h deleted file mode 100755 index 084f424..0000000 --- a/Pods/SDWebImage/SDWebImage/UIImage+GIF.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIImage+GIF.h -// LBGIFImage -// -// Created by Laurin Brandner on 06.01.12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import - -@interface UIImage (GIF) - -+ (UIImage *)sd_animatedGIFNamed:(NSString *)name; - -+ (UIImage *)sd_animatedGIFWithData:(NSData *)data; - -- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size; - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIImage+GIF.m b/Pods/SDWebImage/SDWebImage/UIImage+GIF.m deleted file mode 100755 index e1ded2a..0000000 --- a/Pods/SDWebImage/SDWebImage/UIImage+GIF.m +++ /dev/null @@ -1,158 +0,0 @@ -// -// UIImage+GIF.m -// LBGIFImage -// -// Created by Laurin Brandner on 06.01.12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "UIImage+GIF.h" -#import - -@implementation UIImage (GIF) - -+ (UIImage *)sd_animatedGIFWithData:(NSData *)data { - if (!data) { - return nil; - } - - CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); - - size_t count = CGImageSourceGetCount(source); - - UIImage *animatedImage; - - if (count <= 1) { - animatedImage = [[UIImage alloc] initWithData:data]; - } - else { - NSMutableArray *images = [NSMutableArray array]; - - NSTimeInterval duration = 0.0f; - - for (size_t i = 0; i < count; i++) { - CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL); - - duration += [self sd_frameDurationAtIndex:i source:source]; - - [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]]; - - CGImageRelease(image); - } - - if (!duration) { - duration = (1.0f / 10.0f) * count; - } - - animatedImage = [UIImage animatedImageWithImages:images duration:duration]; - } - - CFRelease(source); - - return animatedImage; -} - -+ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source { - float frameDuration = 0.1f; - CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil); - NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties; - NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; - - NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; - if (delayTimeUnclampedProp) { - frameDuration = [delayTimeUnclampedProp floatValue]; - } - else { - - NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; - if (delayTimeProp) { - frameDuration = [delayTimeProp floatValue]; - } - } - - // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. - // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify - // a duration of <= 10 ms. See and - // for more information. - - if (frameDuration < 0.011f) { - frameDuration = 0.100f; - } - - CFRelease(cfFrameProperties); - return frameDuration; -} - -+ (UIImage *)sd_animatedGIFNamed:(NSString *)name { - CGFloat scale = [UIScreen mainScreen].scale; - - if (scale > 1.0f) { - NSString *retinaPath = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"gif"]; - - NSData *data = [NSData dataWithContentsOfFile:retinaPath]; - - if (data) { - return [UIImage sd_animatedGIFWithData:data]; - } - - NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"]; - - data = [NSData dataWithContentsOfFile:path]; - - if (data) { - return [UIImage sd_animatedGIFWithData:data]; - } - - return [UIImage imageNamed:name]; - } - else { - NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"]; - - NSData *data = [NSData dataWithContentsOfFile:path]; - - if (data) { - return [UIImage sd_animatedGIFWithData:data]; - } - - return [UIImage imageNamed:name]; - } -} - -- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size { - if (CGSizeEqualToSize(self.size, size) || CGSizeEqualToSize(size, CGSizeZero)) { - return self; - } - - CGSize scaledSize = size; - CGPoint thumbnailPoint = CGPointZero; - - CGFloat widthFactor = size.width / self.size.width; - CGFloat heightFactor = size.height / self.size.height; - CGFloat scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor; - scaledSize.width = self.size.width * scaleFactor; - scaledSize.height = self.size.height * scaleFactor; - - if (widthFactor > heightFactor) { - thumbnailPoint.y = (size.height - scaledSize.height) * 0.5; - } - else if (widthFactor < heightFactor) { - thumbnailPoint.x = (size.width - scaledSize.width) * 0.5; - } - - NSMutableArray *scaledImages = [NSMutableArray array]; - - for (UIImage *image in self.images) { - UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); - - [image drawInRect:CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledSize.width, scaledSize.height)]; - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); - - [scaledImages addObject:newImage]; - - UIGraphicsEndImageContext(); - } - - return [UIImage animatedImageWithImages:scaledImages duration:self.duration]; -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.h b/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.h deleted file mode 100644 index 186ebc0..0000000 --- a/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// UIImage+MultiFormat.h -// SDWebImage -// -// Created by Olivier Poitrey on 07/06/13. -// Copyright (c) 2013 Dailymotion. All rights reserved. -// - -#import - -@interface UIImage (MultiFormat) - -+ (UIImage *)sd_imageWithData:(NSData *)data; - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.m b/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.m deleted file mode 100644 index a830754..0000000 --- a/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.m +++ /dev/null @@ -1,118 +0,0 @@ -// -// UIImage+MultiFormat.m -// SDWebImage -// -// Created by Olivier Poitrey on 07/06/13. -// Copyright (c) 2013 Dailymotion. All rights reserved. -// - -#import "UIImage+MultiFormat.h" -#import "UIImage+GIF.h" -#import "NSData+ImageContentType.h" -#import - -#ifdef SD_WEBP -#import "UIImage+WebP.h" -#endif - -@implementation UIImage (MultiFormat) - -+ (UIImage *)sd_imageWithData:(NSData *)data { - if (!data) { - return nil; - } - - UIImage *image; - NSString *imageContentType = [NSData sd_contentTypeForImageData:data]; - if ([imageContentType isEqualToString:@"image/gif"]) { - image = [UIImage sd_animatedGIFWithData:data]; - } -#ifdef SD_WEBP - else if ([imageContentType isEqualToString:@"image/webp"]) - { - image = [UIImage sd_imageWithWebPData:data]; - } -#endif - else { - image = [[UIImage alloc] initWithData:data]; - UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data]; - if (orientation != UIImageOrientationUp) { - image = [UIImage imageWithCGImage:image.CGImage - scale:image.scale - orientation:orientation]; - } - } - - - return image; -} - - -+(UIImageOrientation)sd_imageOrientationFromImageData:(NSData *)imageData { - UIImageOrientation result = UIImageOrientationUp; - CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); - if (imageSource) { - CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); - if (properties) { - CFTypeRef val; - int exifOrientation; - val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); - if (val) { - CFNumberGetValue(val, kCFNumberIntType, &exifOrientation); - result = [self sd_exifOrientationToiOSOrientation:exifOrientation]; - } // else - if it's not set it remains at up - CFRelease((CFTypeRef) properties); - } else { - //NSLog(@"NO PROPERTIES, FAIL"); - } - CFRelease(imageSource); - } - return result; -} - -#pragma mark EXIF orientation tag converter -// Convert an EXIF image orientation to an iOS one. -// reference see here: http://sylvana.net/jpegcrop/exif_orientation.html -+ (UIImageOrientation) sd_exifOrientationToiOSOrientation:(int)exifOrientation { - UIImageOrientation orientation = UIImageOrientationUp; - switch (exifOrientation) { - case 1: - orientation = UIImageOrientationUp; - break; - - case 3: - orientation = UIImageOrientationDown; - break; - - case 8: - orientation = UIImageOrientationLeft; - break; - - case 6: - orientation = UIImageOrientationRight; - break; - - case 2: - orientation = UIImageOrientationUpMirrored; - break; - - case 4: - orientation = UIImageOrientationDownMirrored; - break; - - case 5: - orientation = UIImageOrientationLeftMirrored; - break; - - case 7: - orientation = UIImageOrientationRightMirrored; - break; - default: - break; - } - return orientation; -} - - - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h b/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h deleted file mode 100644 index c1d8fea..0000000 --- a/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import -#import "SDWebImageCompat.h" -#import "SDWebImageManager.h" - -/** - * Integrates SDWebImage async downloading and caching of remote images with UIImageView for highlighted state. - */ -@interface UIImageView (HighlightedWebCache) - -/** - * Set the imageView `highlightedImage` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - */ -- (void)sd_setHighlightedImageWithURL:(NSURL *)url; - -/** - * Set the imageView `highlightedImage` with an `url` and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - */ -- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options; - -/** - * Set the imageView `highlightedImage` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the imageView `highlightedImage` with an `url` and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the imageView `highlightedImage` with an `url` and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param progressBlock A block called while image is downloading - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Cancel the current download - */ -- (void)sd_cancelCurrentHighlightedImageLoad; - -@end - - -@interface UIImageView (HighlightedWebCacheDeprecated) - -- (void)setHighlightedImageWithURL:(NSURL *)url __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:`"); -- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:`"); -- (void)setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:completed:`"); -- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:completed:`"); -- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:progress:completed:`"); - -- (void)cancelCurrentHighlightedImageLoad __deprecated_msg("Use `sd_cancelCurrentHighlightedImageLoad`"); - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.m b/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.m deleted file mode 100644 index 921134e..0000000 --- a/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.m +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "UIImageView+HighlightedWebCache.h" -#import "UIView+WebCacheOperation.h" - -#define UIImageViewHighlightedWebCacheOperationKey @"highlightedImage" - -@implementation UIImageView (HighlightedWebCache) - -- (void)sd_setHighlightedImageWithURL:(NSURL *)url { - [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil]; -} - -- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options { - [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil]; -} - -- (void)sd_setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:completedBlock]; -} - -- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:completedBlock]; -} - -- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_cancelCurrentHighlightedImageLoad]; - - if (url) { - __weak __typeof(self)wself = self; - id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { - if (!wself) return; - dispatch_main_sync_safe (^ - { - if (!wself) return; - if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) - { - completedBlock(image, error, cacheType, url); - return; - } - else if (image) { - wself.highlightedImage = image; - [wself setNeedsLayout]; - } - if (completedBlock && finished) { - completedBlock(image, error, cacheType, url); - } - }); - }]; - [self sd_setImageLoadOperation:operation forKey:UIImageViewHighlightedWebCacheOperationKey]; - } else { - dispatch_main_async_safe(^{ - NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; - if (completedBlock) { - completedBlock(nil, error, SDImageCacheTypeNone, url); - } - }); - } -} - -- (void)sd_cancelCurrentHighlightedImageLoad { - [self sd_cancelImageLoadOperationWithKey:UIImageViewHighlightedWebCacheOperationKey]; -} - -@end - - -@implementation UIImageView (HighlightedWebCacheDeprecated) - -- (void)setHighlightedImageWithURL:(NSURL *)url { - [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil]; -} - -- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options { - [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil]; -} - -- (void)setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setHighlightedImageWithURL:url options:0 progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)cancelCurrentHighlightedImageLoad { - [self sd_cancelCurrentHighlightedImageLoad]; -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.h b/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.h deleted file mode 100644 index eeb7460..0000000 --- a/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "SDWebImageCompat.h" -#import "SDWebImageManager.h" - -/** - * Integrates SDWebImage async downloading and caching of remote images with UIImageView. - * - * Usage with a UITableViewCell sub-class: - * - * @code - -#import - -... - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - static NSString *MyIdentifier = @"MyIdentifier"; - - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; - - if (cell == nil) { - cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] - autorelease]; - } - - // Here we use the provided sd_setImageWithURL: method to load the web image - // Ensure you use a placeholder image otherwise cells will be initialized with no image - [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://example.com/image.jpg"] - placeholderImage:[UIImage imageNamed:@"placeholder"]]; - - cell.textLabel.text = @"My Text"; - return cell; -} - - * @endcode - */ -@interface UIImageView (WebCache) - -/** - * Get the current image URL. - * - * Note that because of the limitations of categories this property can get out of sync - * if you use sd_setImage: directly. - */ -- (NSURL *)sd_imageURL; - -/** - * Set the imageView `image` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - */ -- (void)sd_setImageWithURL:(NSURL *)url; - -/** - * Set the imageView `image` with an `url` and a placeholder. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @see sd_setImageWithURL:placeholderImage:options: - */ -- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - */ -- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; - -/** - * Set the imageView `image` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param progressBlock A block called while image is downloading - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url` and optionally a placeholder image. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param progressBlock A block called while image is downloading - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithPreviousCachedImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock; - -/** - * Download an array of images and starts them in an animation loop - * - * @param arrayOfURLs An array of NSURL - */ -- (void)sd_setAnimationImagesWithURLs:(NSArray *)arrayOfURLs; - -/** - * Cancel the current download - */ -- (void)sd_cancelCurrentImageLoad; - -- (void)sd_cancelCurrentAnimationImagesLoad; - -/** - * Show activity UIActivityIndicatorView - */ -- (void)setShowActivityIndicatorView:(BOOL)show; - -/** - * set desired UIActivityIndicatorViewStyle - * - * @param style The style of the UIActivityIndicatorView - */ -- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style; - -@end - - -@interface UIImageView (WebCacheDeprecated) - -- (NSURL *)imageURL __deprecated_msg("Use `sd_imageURL`"); - -- (void)setImageWithURL:(NSURL *)url __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:`"); -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:`"); -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options`"); - -- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:completed:`"); -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:completed:`"); -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options:completed:`"); -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options:progress:completed:`"); - -- (void)setAnimationImagesWithURLs:(NSArray *)arrayOfURLs __deprecated_msg("Use `sd_setAnimationImagesWithURLs:`"); - -- (void)cancelCurrentArrayLoad __deprecated_msg("Use `sd_cancelCurrentAnimationImagesLoad`"); - -- (void)cancelCurrentImageLoad __deprecated_msg("Use `sd_cancelCurrentImageLoad`"); - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.m b/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.m deleted file mode 100644 index 14f425c..0000000 --- a/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.m +++ /dev/null @@ -1,277 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "UIImageView+WebCache.h" -#import "objc/runtime.h" -#import "UIView+WebCacheOperation.h" - -static char imageURLKey; -static char TAG_ACTIVITY_INDICATOR; -static char TAG_ACTIVITY_STYLE; -static char TAG_ACTIVITY_SHOW; - -@implementation UIImageView (WebCache) - -- (void)sd_setImageWithURL:(NSURL *)url { - [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder { - [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { - [self sd_cancelCurrentImageLoad]; - objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - if (!(options & SDWebImageDelayPlaceholder)) { - dispatch_main_async_safe(^{ - self.image = placeholder; - }); - } - - if (url) { - - // check if activityView is enabled or not - if ([self showActivityIndicatorView]) { - [self addActivityIndicator]; - } - - __weak __typeof(self)wself = self; - id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { - [wself removeActivityIndicator]; - if (!wself) return; - dispatch_main_sync_safe(^{ - if (!wself) return; - if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) - { - completedBlock(image, error, cacheType, url); - return; - } - else if (image) { - wself.image = image; - [wself setNeedsLayout]; - } else { - if ((options & SDWebImageDelayPlaceholder)) { - wself.image = placeholder; - [wself setNeedsLayout]; - } - } - if (completedBlock && finished) { - completedBlock(image, error, cacheType, url); - } - }); - }]; - [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"]; - } else { - dispatch_main_async_safe(^{ - [self removeActivityIndicator]; - if (completedBlock) { - NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; - completedBlock(nil, error, SDImageCacheTypeNone, url); - } - }); - } -} - -- (void)sd_setImageWithPreviousCachedImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { - NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url]; - UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:key]; - - [self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock]; -} - -- (NSURL *)sd_imageURL { - return objc_getAssociatedObject(self, &imageURLKey); -} - -- (void)sd_setAnimationImagesWithURLs:(NSArray *)arrayOfURLs { - [self sd_cancelCurrentAnimationImagesLoad]; - __weak __typeof(self)wself = self; - - NSMutableArray *operationsArray = [[NSMutableArray alloc] init]; - - for (NSURL *logoImageURL in arrayOfURLs) { - id operation = [SDWebImageManager.sharedManager downloadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { - if (!wself) return; - dispatch_main_sync_safe(^{ - __strong UIImageView *sself = wself; - [sself stopAnimating]; - if (sself && image) { - NSMutableArray *currentImages = [[sself animationImages] mutableCopy]; - if (!currentImages) { - currentImages = [[NSMutableArray alloc] init]; - } - [currentImages addObject:image]; - - sself.animationImages = currentImages; - [sself setNeedsLayout]; - } - [sself startAnimating]; - }); - }]; - [operationsArray addObject:operation]; - } - - [self sd_setImageLoadOperation:[NSArray arrayWithArray:operationsArray] forKey:@"UIImageViewAnimationImages"]; -} - -- (void)sd_cancelCurrentImageLoad { - [self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"]; -} - -- (void)sd_cancelCurrentAnimationImagesLoad { - [self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"]; -} - - -#pragma mark - -- (UIActivityIndicatorView *)activityIndicator { - return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR); -} - -- (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator { - objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN); -} - -- (void)setShowActivityIndicatorView:(BOOL)show{ - objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, [NSNumber numberWithBool:show], OBJC_ASSOCIATION_RETAIN); -} - -- (BOOL)showActivityIndicatorView{ - return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue]; -} - -- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style{ - objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber numberWithInt:style], OBJC_ASSOCIATION_RETAIN); -} - -- (int)getIndicatorStyle{ - return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue]; -} - -- (void)addActivityIndicator { - if (!self.activityIndicator) { - self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self getIndicatorStyle]]; - self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO; - - dispatch_main_async_safe(^{ - [self addSubview:self.activityIndicator]; - - [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator - attribute:NSLayoutAttributeCenterX - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeCenterX - multiplier:1.0 - constant:0.0]]; - [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator - attribute:NSLayoutAttributeCenterY - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeCenterY - multiplier:1.0 - constant:0.0]]; - }); - } - - dispatch_main_async_safe(^{ - [self.activityIndicator startAnimating]; - }); - -} - -- (void)removeActivityIndicator { - if (self.activityIndicator) { - [self.activityIndicator removeFromSuperview]; - self.activityIndicator = nil; - } -} - -@end - - -@implementation UIImageView (WebCacheDeprecated) - -- (NSURL *)imageURL { - return [self sd_imageURL]; -} - -- (void)setImageWithURL:(NSURL *)url { - [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; -} - -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder { - [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil]; -} - -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil]; -} - -- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType); - } - }]; -} - -- (void)cancelCurrentArrayLoad { - [self sd_cancelCurrentAnimationImagesLoad]; -} - -- (void)cancelCurrentImageLoad { - [self sd_cancelCurrentImageLoad]; -} - -- (void)setAnimationImagesWithURLs:(NSArray *)arrayOfURLs { - [self sd_setAnimationImagesWithURLs:arrayOfURLs]; -} - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.h b/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.h deleted file mode 100644 index 6719036..0000000 --- a/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import -#import "SDWebImageManager.h" - -@interface UIView (WebCacheOperation) - -/** - * Set the image load operation (storage in a UIView based dictionary) - * - * @param operation the operation - * @param key key for storing the operation - */ -- (void)sd_setImageLoadOperation:(id)operation forKey:(NSString *)key; - -/** - * Cancel all operations for the current UIView and key - * - * @param key key for identifying the operations - */ -- (void)sd_cancelImageLoadOperationWithKey:(NSString *)key; - -/** - * Just remove the operations corresponding to the current UIView and key without cancelling them - * - * @param key key for identifying the operations - */ -- (void)sd_removeImageLoadOperationWithKey:(NSString *)key; - -@end diff --git a/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.m b/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.m deleted file mode 100644 index 9219478..0000000 --- a/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.m +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "UIView+WebCacheOperation.h" -#import "objc/runtime.h" - -static char loadOperationKey; - -@implementation UIView (WebCacheOperation) - -- (NSMutableDictionary *)operationDictionary { - NSMutableDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey); - if (operations) { - return operations; - } - operations = [NSMutableDictionary dictionary]; - objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - return operations; -} - -- (void)sd_setImageLoadOperation:(id)operation forKey:(NSString *)key { - [self sd_cancelImageLoadOperationWithKey:key]; - NSMutableDictionary *operationDictionary = [self operationDictionary]; - [operationDictionary setObject:operation forKey:key]; -} - -- (void)sd_cancelImageLoadOperationWithKey:(NSString *)key { - // Cancel in progress downloader from queue - NSMutableDictionary *operationDictionary = [self operationDictionary]; - id operations = [operationDictionary objectForKey:key]; - if (operations) { - if ([operations isKindOfClass:[NSArray class]]) { - for (id operation in operations) { - if (operation) { - [operation cancel]; - } - } - } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){ - [(id) operations cancel]; - } - [operationDictionary removeObjectForKey:key]; - } -} - -- (void)sd_removeImageLoadOperationWithKey:(NSString *)key { - NSMutableDictionary *operationDictionary = [self operationDictionary]; - [operationDictionary removeObjectForKey:key]; -} - -@end diff --git a/Pods/SVProgressHUD/LICENSE.txt b/Pods/SVProgressHUD/LICENSE.txt deleted file mode 100644 index 7c5f87f..0000000 --- a/Pods/SVProgressHUD/LICENSE.txt +++ /dev/null @@ -1,26 +0,0 @@ -Copyright (c) 2011-2016 Sam Vermette, Tobias Tiemerding and contributors. - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -A different license may apply to other resources included in this package, -including Freepik Icons. Please consult their -respective headers for the terms of their individual licenses. diff --git a/Pods/SVProgressHUD/README.md b/Pods/SVProgressHUD/README.md deleted file mode 100644 index 753db7e..0000000 --- a/Pods/SVProgressHUD/README.md +++ /dev/null @@ -1,196 +0,0 @@ -# SVProgressHUD - -![Pod Version](https://img.shields.io/cocoapods/v/SVProgressHUD.svg?style=flat) -![Pod License](https://img.shields.io/cocoapods/l/SVProgressHUD.svg?style=flat) -![Pod Platform](https://img.shields.io/cocoapods/p/SVProgressHUD.svg?style=flat) -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) - -`SVProgressHUD` is a clean and easy-to-use HUD meant to display the progress of an ongoing task on iOS and tvOS. - -![SVProgressHUD](http://f.cl.ly/items/2G1F1Z0M0k0h2U3V1p39/SVProgressHUD.gif) - -## Demo - -Try `SVProgressHUD` on [Appetize.io](https://appetize.io/app/p8r2cvy8kq74x7q7tjqf5gyatr). - -## Installation - -### From CocoaPods - -[CocoaPods](http://cocoapods.org) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like `SVProgressHUD` in your projects. First, add the following line to your [Podfile](http://guides.cocoapods.org/using/using-cocoapods.html): - -```ruby -pod 'SVProgressHUD' -``` - -If you want to use the latest features of `SVProgressHUD` use normal external source dependencies. - -```ruby -pod 'SVProgressHUD', :git => 'https://github.com/SVProgressHUD/SVProgressHUD.git' -``` - -This pulls from the `master` branch directly. We are usually careful about what we push there and this is the version we use ourselves in all of our projects. - -Second, install `SVProgressHUD` into your project: - -```ruby -pod install -``` - -### Carthage - -[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. - -You can install Carthage with [Homebrew](http://brew.sh/) using the following command: - -```bash -$ brew update -$ brew install carthage -``` - -To integrate `SVProgressHUD` into your Xcode project using Carthage, specify it in your `Cartfile`: - -```ogdl -github "SVProgressHUD/SVProgressHUD" -``` - -Run `carthage update` to build the framework and drag the built `SVProgressHUD.framework` (in Carthage/Build/iOS folder) into your Xcode project (Linked Frameworks and Libraries in `Targets`). - - -### Manually - -* Drag the `SVProgressHUD/SVProgressHUD` folder into your project. -* Take care that `SVProgressHUD.bundle` is added to `Targets->Build Phases->Copy Bundle Resources`. -* Add the **QuartzCore** framework to your project. - -## Usage - -(see sample Xcode project in `/Demo`) - -`SVProgressHUD` is created as a singleton (i.e. it doesn't need to be explicitly allocated and instantiated; you directly call `[SVProgressHUD method]`). - -**Use `SVProgressHUD` wisely! Only use it if you absolutely need to perform a task before taking the user forward. Bad use case examples: pull to refresh, infinite scrolling, sending message.** - -Using `SVProgressHUD` in your app will usually look as simple as this (using Grand Central Dispatch): - -```objective-c -[SVProgressHUD show]; -dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - // time-consuming task - dispatch_async(dispatch_get_main_queue(), ^{ - [SVProgressHUD dismiss]; - }); -}); -``` - -### Showing the HUD - -You can show the status of indeterminate tasks using one of the following: - -```objective-c -+ (void)show; -+ (void)showWithStatus:(NSString*)string; -``` - -If you'd like the HUD to reflect the progress of a task, use one of these: - -```objective-c -+ (void)showProgress:(CGFloat)progress; -+ (void)showProgress:(CGFloat)progress status:(NSString*)status; -``` - -### Dismissing the HUD - -The HUD can be dismissed using: - -```objective-c -+ (void)dismiss; -+ (void)dismissWithDelay:(NSTimeInterval)delay; -``` - -If you'd like to stack HUDs, you can balance out every show call using: - -```objective-c -+ (void)popActivity; -``` - -The HUD will get dismissed once the `popActivity` calls will match the number of show calls. - -Or show a confirmation glyph before before getting dismissed a little bit later. The display time depends on `minimumDismissTimeInterval` and the length of the given string. - -```objective-c -+ (void)showInfoWithStatus:(NSString*)string; -+ (void)showSuccessWithStatus:(NSString*)string; -+ (void)showErrorWithStatus:(NSString*)string; -+ (void)showImage:(UIImage*)image status:(NSString*)string; -``` - -## Customization - -`SVProgressHUD` can be customized via the following methods: - -```objective-c -+ (void)setDefaultStyle:(SVProgressHUDStyle)style; // default is SVProgressHUDStyleLight -+ (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType; // default is SVProgressHUDMaskTypeNone -+ (void)setDefaultAnimationType:(SVProgressHUDAnimationType)type; // default is SVProgressHUDAnimationTypeFlat -+ (void)setMinimumSize:(CGSize)minimumSize; // default is CGSizeZero, can be used to avoid resizing for a larger message -+ (void)setRingThickness:(CGFloat)width; // default is 2 pt -+ (void)setRingRadius:(CGFloat)radius; // default is 18 pt -+ (void)setRingNoTextRadius:(CGFloat)radius; // default is 24 pt -+ (void)setCornerRadius:(CGFloat)cornerRadius; // default is 14 pt -+ (void)setFont:(UIFont*)font; // default is [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline] -+ (void)setForegroundColor:(UIColor*)color; // default is [UIColor blackColor], only used for SVProgressHUDStyleCustom -+ (void)setBackgroundColor:(UIColor*)color; // default is [UIColor whiteColor], only used for SVProgressHUDStyleCustom -+ (void)setBackgroundLayerColor:(UIColor*)color; // default is [UIColor colorWithWhite:0 alpha:0.4], only used for SVProgressHUDMaskTypeCustom -+ (void)setInfoImage:(UIImage*)image; // default is the bundled info image provided by Freepik -+ (void)setSuccessImage:(UIImage*)image; // default is bundled success image from Freepik -+ (void)setErrorImage:(UIImage*)image; // default is bundled error image from Freepik -+ (void)setViewForExtension:(UIView*)view; // default is nil, only used if #define SV_APP_EXTENSIONS is set -+ (void)setMinimumDismissTimeInterval:(NSTimeInterval)interval; // default is 5.0 seconds -+ (void)setFadeInAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds -+ (void)setFadeOutAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds -``` - -Additionally `SVProgressHUD` supports the `UIAppearance` protocol for most of the above methods. - -### Hint - -As standard `SVProgressHUD` offers two preconfigured styles: - -* `SVProgressHUDStyleLight`: White background with black spinner and text -* `SVProgressHUDStyleDark`: Black background with white spinner and text - -If you want to use custom colors with `setForegroundColor` and `setBackgroundColor:` don't forget to set `SVProgressHUDStyleCustom` via `setDefaultStyle:`. - -## Notifications - -`SVProgressHUD` posts four notifications via `NSNotificationCenter` in response to being shown/dismissed: -* `SVProgressHUDWillAppearNotification` when the show animation starts -* `SVProgressHUDDidAppearNotification` when the show animation completes -* `SVProgressHUDWillDisappearNotification` when the dismiss animation starts -* `SVProgressHUDDidDisappearNotification` when the dismiss animation completes - -Each notification passes a `userInfo` dictionary holding the HUD's status string (if any), retrievable via `SVProgressHUDStatusUserInfoKey`. - -`SVProgressHUD` also posts `SVProgressHUDDidReceiveTouchEventNotification` when users touch on the overall screen or `SVProgressHUDDidTouchDownInsideNotification` when a user touches on the HUD directly. For this notifications `userInfo` is not passed but the object parameter contains the `UIEvent` that related to the touch. - -## App Extensions - -When using `SVProgressHUD` in an App Extension, `#define SV_APP_EXTENSIONS` to avoid using unavailable APIs. Additionally call `setViewForExtension:` from your extensions view controller with `self.view`. - -## Contributing to this project - -If you have feature requests or bug reports, feel free to help out by sending pull requests or by [creating new issues](https://github.com/SVProgressHUD/SVProgressHUD/issues/new). Please take a moment to -review the guidelines written by [Nicolas Gallagher](https://github.com/necolas): - -* [Bug reports](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md#bugs) -* [Feature requests](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md#features) -* [Pull requests](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md#pull-requests) - -## License - -`SVProgressHUD` is distributed under the terms and conditions of the [MIT license](https://github.com/SVProgressHUD/SVProgressHUD/blob/master/LICENSE.txt). The success, error and info icons are made by [Freepik](http://www.freepik.com) from [Flaticon](http://www.flaticon.com) and are licensed under [Creative Commons BY 3.0](http://creativecommons.org/licenses/by/3.0/). - -## Credits - -`SVProgressHUD` is brought to you by [Sam Vermette](http://samvermette.com), [Tobias Tiemerding](http://tiemerding.com) and [contributors to the project](https://github.com/SVProgressHUD/SVProgressHUD/contributors). If you're using `SVProgressHUD` in your project, attribution would be very appreciated. diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.h b/Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.h deleted file mode 100644 index d6f1eaf..0000000 --- a/Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// SVIndefiniteAnimatedView.h -// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD -// -// Copyright (c) 2014-2016 Guillaume Campagna. All rights reserved. -// - -#import - -@interface SVIndefiniteAnimatedView : UIView - -@property (nonatomic, assign) CGFloat strokeThickness; -@property (nonatomic, assign) CGFloat radius; -@property (nonatomic, strong) UIColor *strokeColor; - -@end - diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.m b/Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.m deleted file mode 100644 index 235a0f9..0000000 --- a/Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.m +++ /dev/null @@ -1,137 +0,0 @@ -// -// SVIndefiniteAnimatedView.m -// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD -// -// Copyright (c) 2014-2016 Guillaume Campagna. All rights reserved. -// - -#import "SVIndefiniteAnimatedView.h" -#import "SVProgressHUD.h" - -@interface SVIndefiniteAnimatedView () - -@property (nonatomic, strong) CAShapeLayer *indefiniteAnimatedLayer; - -@end - -@implementation SVIndefiniteAnimatedView - -- (void)willMoveToSuperview:(UIView*)newSuperview { - if (newSuperview) { - [self layoutAnimatedLayer]; - } else { - [_indefiniteAnimatedLayer removeFromSuperlayer]; - _indefiniteAnimatedLayer = nil; - } -} - -- (void)layoutAnimatedLayer { - CALayer *layer = self.indefiniteAnimatedLayer; - [self.layer addSublayer:layer]; - - CGFloat widthDiff = CGRectGetWidth(self.bounds) - CGRectGetWidth(layer.bounds); - CGFloat heightDiff = CGRectGetHeight(self.bounds) - CGRectGetHeight(layer.bounds); - layer.position = CGPointMake(CGRectGetWidth(self.bounds) - CGRectGetWidth(layer.bounds) / 2 - widthDiff / 2, CGRectGetHeight(self.bounds) - CGRectGetHeight(layer.bounds) / 2 - heightDiff / 2); -} - -- (CAShapeLayer*)indefiniteAnimatedLayer { - if(!_indefiniteAnimatedLayer) { - CGPoint arcCenter = CGPointMake(self.radius+self.strokeThickness/2+5, self.radius+self.strokeThickness/2+5); - UIBezierPath* smoothedPath = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:self.radius startAngle:(CGFloat) (M_PI*3/2) endAngle:(CGFloat) (M_PI/2+M_PI*5) clockwise:YES]; - - _indefiniteAnimatedLayer = [CAShapeLayer layer]; - _indefiniteAnimatedLayer.contentsScale = [[UIScreen mainScreen] scale]; - _indefiniteAnimatedLayer.frame = CGRectMake(0.0f, 0.0f, arcCenter.x*2, arcCenter.y*2); - _indefiniteAnimatedLayer.fillColor = [UIColor clearColor].CGColor; - _indefiniteAnimatedLayer.strokeColor = self.strokeColor.CGColor; - _indefiniteAnimatedLayer.lineWidth = self.strokeThickness; - _indefiniteAnimatedLayer.lineCap = kCALineCapRound; - _indefiniteAnimatedLayer.lineJoin = kCALineJoinBevel; - _indefiniteAnimatedLayer.path = smoothedPath.CGPath; - - CALayer *maskLayer = [CALayer layer]; - - NSBundle *bundle = [NSBundle bundleForClass:[SVProgressHUD class]]; - NSURL *url = [bundle URLForResource:@"SVProgressHUD" withExtension:@"bundle"]; - NSBundle *imageBundle = [NSBundle bundleWithURL:url]; - - NSString *path = [imageBundle pathForResource:@"angle-mask" ofType:@"png"]; - - maskLayer.contents = (__bridge id)[[UIImage imageWithContentsOfFile:path] CGImage]; - maskLayer.frame = _indefiniteAnimatedLayer.bounds; - _indefiniteAnimatedLayer.mask = maskLayer; - - NSTimeInterval animationDuration = 1; - CAMediaTimingFunction *linearCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - - CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; - animation.fromValue = (id) 0; - animation.toValue = @(M_PI*2); - animation.duration = animationDuration; - animation.timingFunction = linearCurve; - animation.removedOnCompletion = NO; - animation.repeatCount = INFINITY; - animation.fillMode = kCAFillModeForwards; - animation.autoreverses = NO; - [_indefiniteAnimatedLayer.mask addAnimation:animation forKey:@"rotate"]; - - CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; - animationGroup.duration = animationDuration; - animationGroup.repeatCount = INFINITY; - animationGroup.removedOnCompletion = NO; - animationGroup.timingFunction = linearCurve; - - CABasicAnimation *strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"]; - strokeStartAnimation.fromValue = @0.015; - strokeStartAnimation.toValue = @0.515; - - CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; - strokeEndAnimation.fromValue = @0.485; - strokeEndAnimation.toValue = @0.985; - - animationGroup.animations = @[strokeStartAnimation, strokeEndAnimation]; - [_indefiniteAnimatedLayer addAnimation:animationGroup forKey:@"progress"]; - - } - return _indefiniteAnimatedLayer; -} - -- (void)setFrame:(CGRect)frame { - if(!CGRectEqualToRect(frame, super.frame)) { - [super setFrame:frame]; - - if(self.superview) { - [self layoutAnimatedLayer]; - } - } - -} - -- (void)setRadius:(CGFloat)radius { - if(radius != _radius) { - _radius = radius; - - [_indefiniteAnimatedLayer removeFromSuperlayer]; - _indefiniteAnimatedLayer = nil; - - if(self.superview) { - [self layoutAnimatedLayer]; - } - } -} - -- (void)setStrokeColor:(UIColor*)strokeColor { - _strokeColor = strokeColor; - _indefiniteAnimatedLayer.strokeColor = strokeColor.CGColor; -} - -- (void)setStrokeThickness:(CGFloat)strokeThickness { - _strokeThickness = strokeThickness; - _indefiniteAnimatedLayer.lineWidth = _strokeThickness; -} - -- (CGSize)sizeThatFits:(CGSize)size { - return CGSizeMake((self.radius+self.strokeThickness/2+5)*2, (self.radius+self.strokeThickness/2+5)*2); -} - -@end diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.h b/Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.h deleted file mode 100644 index 6b4d54a..0000000 --- a/Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// SVProgressAnimatedView.h -// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD -// -// Copyright (c) 2016 Tobias Tiemerding. All rights reserved. -// - -#import - -@interface SVProgressAnimatedView : UIView - -@property (nonatomic, assign) CGFloat radius; -@property (nonatomic, assign) CGFloat strokeThickness; -@property (nonatomic, strong) UIColor *strokeColor; -@property (nonatomic, assign) CGFloat strokeEnd; - -@end diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.m b/Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.m deleted file mode 100644 index 3adb3c6..0000000 --- a/Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.m +++ /dev/null @@ -1,98 +0,0 @@ -// -// SVProgressAnimatedView.m -// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD -// -// Copyright (c) 2016 Tobias Tiemerding. All rights reserved. -// - -#import "SVProgressAnimatedView.h" - -@interface SVProgressAnimatedView () - -@property (nonatomic, strong) CAShapeLayer *ringAnimatedLayer; - -@end - -@implementation SVProgressAnimatedView - -- (void)willMoveToSuperview:(UIView*)newSuperview { - if (newSuperview) { - [self layoutAnimatedLayer]; - } else { - [_ringAnimatedLayer removeFromSuperlayer]; - _ringAnimatedLayer = nil; - } -} - -- (void)layoutAnimatedLayer { - CALayer *layer = self.ringAnimatedLayer; - [self.layer addSublayer:layer]; - - CGFloat widthDiff = CGRectGetWidth(self.bounds) - CGRectGetWidth(layer.bounds); - CGFloat heightDiff = CGRectGetHeight(self.bounds) - CGRectGetHeight(layer.bounds); - layer.position = CGPointMake(CGRectGetWidth(self.bounds) - CGRectGetWidth(layer.bounds) / 2 - widthDiff / 2, CGRectGetHeight(self.bounds) - CGRectGetHeight(layer.bounds) / 2 - heightDiff / 2); -} - -- (CAShapeLayer*)ringAnimatedLayer { - if(!_ringAnimatedLayer) { - CGPoint arcCenter = CGPointMake(self.radius+self.strokeThickness/2+5, self.radius+self.strokeThickness/2+5); - UIBezierPath* smoothedPath = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:self.radius startAngle:(CGFloat)-M_PI_2 endAngle:(CGFloat) (M_PI + M_PI_2) clockwise:YES]; - - _ringAnimatedLayer = [CAShapeLayer layer]; - _ringAnimatedLayer.contentsScale = [[UIScreen mainScreen] scale]; - _ringAnimatedLayer.frame = CGRectMake(0.0f, 0.0f, arcCenter.x*2, arcCenter.y*2); - _ringAnimatedLayer.fillColor = [UIColor clearColor].CGColor; - _ringAnimatedLayer.strokeColor = self.strokeColor.CGColor; - _ringAnimatedLayer.lineWidth = self.strokeThickness; - _ringAnimatedLayer.lineCap = kCALineCapRound; - _ringAnimatedLayer.lineJoin = kCALineJoinBevel; - _ringAnimatedLayer.path = smoothedPath.CGPath; - } - return _ringAnimatedLayer; -} - -- (void)setFrame:(CGRect)frame { - if(!CGRectEqualToRect(frame, super.frame)) { - [super setFrame:frame]; - - if(self.superview) { - [self layoutAnimatedLayer]; - } - } - -} - -- (void)setRadius:(CGFloat)radius { - if(radius != _radius) { - _radius = radius; - - [_ringAnimatedLayer removeFromSuperlayer]; - _ringAnimatedLayer = nil; - - if(self.superview) { - [self layoutAnimatedLayer]; - } - } -} - -- (void)setStrokeColor:(UIColor*)strokeColor { - _strokeColor = strokeColor; - _ringAnimatedLayer.strokeColor = strokeColor.CGColor; -} - -- (void)setStrokeThickness:(CGFloat)strokeThickness { - _strokeThickness = strokeThickness; - _ringAnimatedLayer.lineWidth = _strokeThickness; -} - -- (void)setStrokeEnd:(CGFloat)strokeEnd { - _strokeEnd = strokeEnd; - _ringAnimatedLayer.strokeEnd = _strokeEnd; -} - - -- (CGSize)sizeThatFits:(CGSize)size { - return CGSizeMake((self.radius+self.strokeThickness/2+5)*2, (self.radius+self.strokeThickness/2+5)*2); -} - -@end diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask.png deleted file mode 100644 index 0150a03..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@2x.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@2x.png deleted file mode 100644 index 9a302b6..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@2x.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@3x.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@3x.png deleted file mode 100644 index d07f3ce..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@3x.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error.png deleted file mode 100644 index a57c8e4..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@2x.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@2x.png deleted file mode 100644 index aaf6798..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@2x.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@3x.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@3x.png deleted file mode 100644 index c92518f..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@3x.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info.png deleted file mode 100644 index a3a1f75..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@2x.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@2x.png deleted file mode 100644 index 1b333e7..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@2x.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@3x.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@3x.png deleted file mode 100644 index d56aa0c..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@3x.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success.png deleted file mode 100644 index 44769d0..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@2x.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@2x.png deleted file mode 100644 index a9d1653..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@2x.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@3x.png b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@3x.png deleted file mode 100644 index 42bad9b..0000000 Binary files a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@3x.png and /dev/null differ diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.h b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.h deleted file mode 100644 index fdd03aa..0000000 --- a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.h +++ /dev/null @@ -1,131 +0,0 @@ -// -// SVProgressHUD.h -// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD -// -// Copyright (c) 2011-2016 Sam Vermette and contributors. All rights reserved. -// - -#import -#import - -#if __IPHONE_OS_VERSION_MAX_ALLOWED < 70000 - -#define UI_APPEARANCE_SELECTOR - -#endif - -extern NSString * const SVProgressHUDDidReceiveTouchEventNotification; -extern NSString * const SVProgressHUDDidTouchDownInsideNotification; -extern NSString * const SVProgressHUDWillDisappearNotification; -extern NSString * const SVProgressHUDDidDisappearNotification; -extern NSString * const SVProgressHUDWillAppearNotification; -extern NSString * const SVProgressHUDDidAppearNotification; - -extern NSString * const SVProgressHUDStatusUserInfoKey; - -typedef NS_ENUM(NSInteger, SVProgressHUDStyle) { - SVProgressHUDStyleLight, // default style, white HUD with black text, HUD background will be blurred on iOS 8 and above - SVProgressHUDStyleDark, // black HUD and white text, HUD background will be blurred on iOS 8 and above - SVProgressHUDStyleCustom // uses the fore- and background color properties -}; - -typedef NS_ENUM(NSUInteger, SVProgressHUDMaskType) { - SVProgressHUDMaskTypeNone = 1, // default mask type, allow user interactions while HUD is displayed - SVProgressHUDMaskTypeClear, // don't allow user interactions - SVProgressHUDMaskTypeBlack, // don't allow user interactions and dim the UI in the back of the HUD, as on iOS 7 and above - SVProgressHUDMaskTypeGradient, // don't allow user interactions and dim the UI with a a-la UIAlertView background gradient, as on iOS 6 - SVProgressHUDMaskTypeCustom // don't allow user interactions and dim the UI in the back of the HUD with a custom color -}; - -typedef NS_ENUM(NSUInteger, SVProgressHUDAnimationType) { - SVProgressHUDAnimationTypeFlat, // default animation type, custom flat animation (indefinite animated ring) - SVProgressHUDAnimationTypeNative // iOS native UIActivityIndicatorView -}; - -@interface SVProgressHUD : UIView - -#pragma mark - Customization - -@property (assign, nonatomic) SVProgressHUDStyle defaultStyle UI_APPEARANCE_SELECTOR; // default is SVProgressHUDStyleLight -@property (assign, nonatomic) SVProgressHUDMaskType defaultMaskType UI_APPEARANCE_SELECTOR; // default is SVProgressHUDMaskTypeNone -@property (assign, nonatomic) SVProgressHUDAnimationType defaultAnimationType UI_APPEARANCE_SELECTOR; // default is SVProgressHUDAnimationTypeFlat -@property (assign, nonatomic) CGSize minimumSize UI_APPEARANCE_SELECTOR; // default is CGSizeZero, can be used to avoid resizing for a larger message -@property (assign, nonatomic) CGFloat ringThickness UI_APPEARANCE_SELECTOR; // default is 2 pt -@property (assign, nonatomic) CGFloat ringRadius UI_APPEARANCE_SELECTOR; // default is 18 pt -@property (assign, nonatomic) CGFloat ringNoTextRadius UI_APPEARANCE_SELECTOR; // default is 24 pt -@property (assign, nonatomic) CGFloat cornerRadius UI_APPEARANCE_SELECTOR; // default is 14 pt -@property (strong, nonatomic) UIFont *font UI_APPEARANCE_SELECTOR; // default is [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline] -@property (strong, nonatomic) UIColor *backgroundColor UI_APPEARANCE_SELECTOR; // default is [UIColor whiteColor] -@property (strong, nonatomic) UIColor *foregroundColor UI_APPEARANCE_SELECTOR; // default is [UIColor blackColor] -@property (strong, nonatomic) UIColor *backgroundLayerColor UI_APPEARANCE_SELECTOR; // default is [UIColor colorWithWhite:0 alpha:0.4] -@property (strong, nonatomic) UIImage *infoImage UI_APPEARANCE_SELECTOR; // default is the bundled info image provided by Freepik -@property (strong, nonatomic) UIImage *successImage UI_APPEARANCE_SELECTOR; // default is the bundled success image provided by Freepik -@property (strong, nonatomic) UIImage *errorImage UI_APPEARANCE_SELECTOR; // default is the bundled error image provided by Freepik -@property (strong, nonatomic) UIView *viewForExtension UI_APPEARANCE_SELECTOR; // default is nil, only used if #define SV_APP_EXTENSIONS is set -@property (assign, nonatomic) NSTimeInterval minimumDismissTimeInterval; // default is 5.0 seconds - -@property (assign, nonatomic) UIOffset offsetFromCenter UI_APPEARANCE_SELECTOR; // default is 0, 0 - -@property (assign, nonatomic) NSTimeInterval fadeInAnimationDuration; // default is 0.15 -@property (assign, nonatomic) NSTimeInterval fadeOutAnimationDuration; // default is 0.15 - - -+ (void)setDefaultStyle:(SVProgressHUDStyle)style; // default is SVProgressHUDStyleLight -+ (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType; // default is SVProgressHUDMaskTypeNone -+ (void)setDefaultAnimationType:(SVProgressHUDAnimationType)type; // default is SVProgressHUDAnimationTypeFlat -+ (void)setMinimumSize:(CGSize)minimumSize; // default is CGSizeZero, can be used to avoid resizing for a larger message -+ (void)setRingThickness:(CGFloat)ringThickness; // default is 2 pt -+ (void)setRingRadius:(CGFloat)radius; // default is 18 pt -+ (void)setRingNoTextRadius:(CGFloat)radius; // default is 24 pt -+ (void)setCornerRadius:(CGFloat)cornerRadius; // default is 14 pt -+ (void)setFont:(UIFont*)font; // default is [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline] -+ (void)setForegroundColor:(UIColor*)color; // default is [UIColor blackColor], only used for SVProgressHUDStyleCustom -+ (void)setBackgroundColor:(UIColor*)color; // default is [UIColor whiteColor], only used for SVProgressHUDStyleCustom -+ (void)setBackgroundLayerColor:(UIColor*)color; // default is [UIColor colorWithWhite:0 alpha:0.5], only used for SVProgressHUDMaskTypeBlack -+ (void)setInfoImage:(UIImage*)image; // default is the bundled info image provided by Freepik -+ (void)setSuccessImage:(UIImage*)image; // default is the bundled success image provided by Freepik -+ (void)setErrorImage:(UIImage*)image; // default is the bundled error image provided by Freepik -+ (void)setViewForExtension:(UIView*)view; // default is nil, only used if #define SV_APP_EXTENSIONS is set -+ (void)setMinimumDismissTimeInterval:(NSTimeInterval)interval; // default is 5.0 seconds -+ (void)setFadeInAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds -+ (void)setFadeOutAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds - -#pragma mark - Show Methods - -+ (void)show; -+ (void)showWithMaskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use show and setDefaultMaskType: instead."))); -+ (void)showWithStatus:(NSString*)status; -+ (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showWithStatus: and setDefaultMaskType: instead."))); - -+ (void)showProgress:(float)progress; -+ (void)showProgress:(float)progress maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showProgress: and setDefaultMaskType: instead."))); -+ (void)showProgress:(float)progress status:(NSString*)status; -+ (void)showProgress:(float)progress status:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showProgress:status: and setDefaultMaskType: instead."))); - -+ (void)setStatus:(NSString*)status; // change the HUD loading status while it's showing - -// stops the activity indicator, shows a glyph + status, and dismisses the HUD a little bit later -+ (void)showInfoWithStatus:(NSString*)status; -+ (void)showInfoWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showInfoWithStatus: and setDefaultMaskType: instead."))); -+ (void)showSuccessWithStatus:(NSString*)status; -+ (void)showSuccessWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showSuccessWithStatus: and setDefaultMaskType: instead."))); -+ (void)showErrorWithStatus:(NSString*)status; -+ (void)showErrorWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showErrorWithStatus: and setDefaultMaskType: instead."))); - -// shows a image + status, use 28x28 white PNGs -+ (void)showImage:(UIImage*)image status:(NSString*)status; -+ (void)showImage:(UIImage*)image status:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showImage:status: and setDefaultMaskType: instead."))); - -+ (void)setOffsetFromCenter:(UIOffset)offset; -+ (void)resetOffsetFromCenter; - -+ (void)popActivity; // decrease activity count, if activity count == 0 the HUD is dismissed -+ (void)dismiss; -+ (void)dismissWithDelay:(NSTimeInterval)delay; - -+ (BOOL)isVisible; - -+ (NSTimeInterval)displayDurationForString:(NSString*)string; - -@end - diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.m b/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.m deleted file mode 100644 index 8441cb7..0000000 --- a/Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.m +++ /dev/null @@ -1,1403 +0,0 @@ -// -// SVProgressHUD.h -// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD -// -// Copyright (c) 2011-2016 Sam Vermette and contributors. All rights reserved. -// - -#if !__has_feature(objc_arc) -#error SVProgressHUD is ARC only. Either turn on ARC for the project or use -fobjc-arc flag -#endif - -#import "SVProgressHUD.h" -#import "SVIndefiniteAnimatedView.h" -#import "SVProgressAnimatedView.h" -#import "SVRadialGradientLayer.h" - -NSString * const SVProgressHUDDidReceiveTouchEventNotification = @"SVProgressHUDDidReceiveTouchEventNotification"; -NSString * const SVProgressHUDDidTouchDownInsideNotification = @"SVProgressHUDDidTouchDownInsideNotification"; -NSString * const SVProgressHUDWillDisappearNotification = @"SVProgressHUDWillDisappearNotification"; -NSString * const SVProgressHUDDidDisappearNotification = @"SVProgressHUDDidDisappearNotification"; -NSString * const SVProgressHUDWillAppearNotification = @"SVProgressHUDWillAppearNotification"; -NSString * const SVProgressHUDDidAppearNotification = @"SVProgressHUDDidAppearNotification"; - -NSString * const SVProgressHUDStatusUserInfoKey = @"SVProgressHUDStatusUserInfoKey"; - -static const CGFloat SVProgressHUDParallaxDepthPoints = 10; -static const CGFloat SVProgressHUDUndefinedProgress = -1; -static const CGFloat SVProgressHUDDefaultAnimationDuration = 0.15; - -@interface SVProgressHUD () - -@property (nonatomic, strong, readonly) NSTimer *fadeOutTimer; -@property (nonatomic, readonly, getter = isClear) BOOL clear; - -@property (nonatomic, strong) UIControl *overlayView; -@property (nonatomic, strong) UIView *hudView; - -@property (nonatomic, strong) UILabel *statusLabel; -@property (nonatomic, strong) UIImageView *imageView; -@property (nonatomic, strong) UIView *indefiniteAnimatedView; -@property (nonatomic, strong) SVProgressAnimatedView *ringView; -@property (nonatomic, strong) SVProgressAnimatedView *backgroundRingView; -@property (nonatomic, strong) CALayer *backgroundLayer; - -@property (nonatomic, readwrite) CGFloat progress; -@property (nonatomic, readwrite) NSUInteger activityCount; - -@property (nonatomic, readonly) CGFloat visibleKeyboardHeight; - -- (void)updateHUDFrame; -- (void)updateMask; -- (void)updateBlurBounds; -#if TARGET_OS_IOS -- (void)updateMotionEffectForOrientation:(UIInterfaceOrientation)orientation; -#endif -- (void)updateMotionEffectForXMotionEffectType:(UIInterpolatingMotionEffectType)xMotionEffectType yMotionEffectType:(UIInterpolatingMotionEffectType)yMotionEffectType; -- (void)updateViewHierachy; - -- (void)setStatus:(NSString*)status; -- (void)setFadeOutTimer:(NSTimer*)timer; - -- (void)registerNotifications; -- (NSDictionary*)notificationUserInfo; - -- (void)positionHUD:(NSNotification*)notification; -- (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle; - -- (void)overlayViewDidReceiveTouchEvent:(id)sender forEvent:(UIEvent*)event; - -- (void)showProgress:(float)progress status:(NSString*)status; -- (void)showImage:(UIImage*)image status:(NSString*)status duration:(NSTimeInterval)duration; -- (void)showStatus:(NSString*)status; - -- (void)dismiss; -- (void)dismissWithDelay:(NSTimeInterval)delay; - -- (UIView*)indefiniteAnimatedView; -- (SVProgressAnimatedView*)ringView; -- (SVProgressAnimatedView*)backgroundRingView; - -- (void)cancelRingLayerAnimation; -- (void)cancelIndefiniteAnimatedViewAnimation; - -- (UIColor*)foregroundColorForStyle; -- (UIColor*)backgroundColorForStyle; -- (UIImage*)image:(UIImage*)image withTintColor:(UIColor*)color; - -@end - - -@implementation SVProgressHUD { - BOOL _isInitializing; -} - -+ (SVProgressHUD*)sharedView { - static dispatch_once_t once; - - static SVProgressHUD *sharedView; -#if !defined(SV_APP_EXTENSIONS) - dispatch_once(&once, ^{ sharedView = [[self alloc] initWithFrame:[[[UIApplication sharedApplication] delegate] window].bounds]; }); -#else - dispatch_once(&once, ^{ sharedView = [[self alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; }); -#endif - return sharedView; -} - - -#pragma mark - Setters - -+ (void)setStatus:(NSString*)status { - [[self sharedView] setStatus:status]; -} - -+ (void)setDefaultStyle:(SVProgressHUDStyle)style { - [self sharedView].defaultStyle = style; -} - -+ (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType { - [self sharedView].defaultMaskType = maskType; -} - -+ (void)setDefaultAnimationType:(SVProgressHUDAnimationType)type { - [self sharedView].defaultAnimationType = type; -} - -+ (void)setMinimumSize:(CGSize)minimumSize { - [self sharedView].minimumSize = minimumSize; -} - -+ (void)setRingThickness:(CGFloat)ringThickness { - [self sharedView].ringThickness = ringThickness; -} - -+ (void)setRingRadius:(CGFloat)radius { - [self sharedView].ringRadius = radius; -} - -+ (void)setRingNoTextRadius:(CGFloat)radius { - [self sharedView].ringNoTextRadius = radius; -} - -+ (void)setCornerRadius:(CGFloat)cornerRadius { - [self sharedView].cornerRadius = cornerRadius; -} - -+ (void)setFont:(UIFont*)font { - [self sharedView].font = font; -} - -+ (void)setForegroundColor:(UIColor*)color { - [self sharedView].foregroundColor = color; -} - -+ (void)setBackgroundColor:(UIColor*)color { - [self sharedView].backgroundColor = color; -} - -+ (void)setBackgroundLayerColor:(UIColor*)color { - [self sharedView].backgroundLayerColor = color; -} - -+ (void)setInfoImage:(UIImage*)image { - [self sharedView].infoImage = image; -} - -+ (void)setSuccessImage:(UIImage*)image { - [self sharedView].successImage = image; -} - -+ (void)setErrorImage:(UIImage*)image { - [self sharedView].errorImage = image; -} - -+ (void)setViewForExtension:(UIView*)view { - [self sharedView].viewForExtension = view; -} - -+ (void)setMinimumDismissTimeInterval:(NSTimeInterval)interval { - [self sharedView].minimumDismissTimeInterval = interval; -} - -+ (void)setFadeInAnimationDuration:(NSTimeInterval)duration { - [self sharedView].fadeInAnimationDuration = duration; -} - -+ (void)setFadeOutAnimationDuration:(NSTimeInterval)duration { - [self sharedView].fadeOutAnimationDuration = duration; -} - - -#pragma mark - Show Methods - -+ (void)show { - [self showWithStatus:nil]; -} - -+ (void)showWithMaskType:(SVProgressHUDMaskType)maskType { - SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType; - [self setDefaultMaskType:maskType]; - [self show]; - [self setDefaultMaskType:existingMaskType]; -} - -+ (void)showWithStatus:(NSString*)status { - [self sharedView]; - [self showProgress:SVProgressHUDUndefinedProgress status:status]; -} - -+ (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType { - SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType; - [self setDefaultMaskType:maskType]; - [self showWithStatus:status]; - [self setDefaultMaskType:existingMaskType]; -} - -+ (void)showProgress:(float)progress { - [self showProgress:progress status:nil]; -} - -+ (void)showProgress:(float)progress maskType:(SVProgressHUDMaskType)maskType { - SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType; - [self setDefaultMaskType:maskType]; - [self showProgress:progress]; - [self setDefaultMaskType:existingMaskType]; -} - -+ (void)showProgress:(float)progress status:(NSString*)status { - [[self sharedView] showProgress:progress status:status]; -} - -+ (void)showProgress:(float)progress status:(NSString*)status maskType:(SVProgressHUDMaskType)maskType { - SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType; - [self setDefaultMaskType:maskType]; - [self showProgress:progress status:status]; - [self setDefaultMaskType:existingMaskType]; -} - - -#pragma mark - Show, then automatically dismiss methods - -+ (void)showInfoWithStatus:(NSString*)status { - [self showImage:[self sharedView].infoImage status:status]; -} - -+ (void)showInfoWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType { - SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType; - [self setDefaultMaskType:maskType]; - [self showInfoWithStatus:status]; - [self setDefaultMaskType:existingMaskType]; -} - -+ (void)showSuccessWithStatus:(NSString*)status { - [self showImage:[self sharedView].successImage status:status]; -} - -+ (void)showSuccessWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType { - SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType; - [self setDefaultMaskType:maskType]; - [self showSuccessWithStatus:status]; - [self setDefaultMaskType:existingMaskType]; -} - -+ (void)showErrorWithStatus:(NSString*)status { - [self showImage:[self sharedView].errorImage status:status]; -} - -+ (void)showErrorWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType { - SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType; - [self setDefaultMaskType:maskType]; - [self showErrorWithStatus:status]; - [self setDefaultMaskType:existingMaskType]; -} - -+ (void)showImage:(UIImage*)image status:(NSString*)status { - NSTimeInterval displayInterval = [self displayDurationForString:status]; - [[self sharedView] showImage:image status:status duration:displayInterval]; -} - -+ (void)showImage:(UIImage*)image status:(NSString*)status maskType:(SVProgressHUDMaskType)maskType { - SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType; - [self setDefaultMaskType:maskType]; - [self showImage:image status:status]; - [self setDefaultMaskType:existingMaskType]; -} - - -#pragma mark - Dismiss Methods - -+ (void)popActivity { - if([self sharedView].activityCount > 0) { - [self sharedView].activityCount--; - } - if([self sharedView].activityCount == 0) { - [[self sharedView] dismiss]; - } -} - -+ (void)dismiss { - [self dismissWithDelay:0.0]; -} - -+ (void)dismissWithDelay:(NSTimeInterval)delay { - [[self sharedView] dismissWithDelay:delay]; -} - - -#pragma mark - Offset - -+ (void)setOffsetFromCenter:(UIOffset)offset { - [self sharedView].offsetFromCenter = offset; -} - -+ (void)resetOffsetFromCenter { - [self setOffsetFromCenter:UIOffsetZero]; -} - - -#pragma mark - Instance Methods - -- (instancetype)initWithFrame:(CGRect)frame { - if((self = [super initWithFrame:frame])) { - _isInitializing = YES; - - self.userInteractionEnabled = NO; - _backgroundColor = [UIColor clearColor]; - _foregroundColor = [UIColor blackColor]; - _backgroundLayerColor = [UIColor colorWithWhite:0 alpha:0.4]; - - self.alpha = 0.0f; - self.activityCount = 0; - - // Set default values - _defaultMaskType = SVProgressHUDMaskTypeNone; - _defaultStyle = SVProgressHUDStyleLight; - _defaultAnimationType = SVProgressHUDAnimationTypeFlat; - - if ([UIFont respondsToSelector:@selector(preferredFontForTextStyle:)]) { - _font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]; - } else { - _font = [UIFont systemFontOfSize:14.0f]; - } - - NSBundle *bundle = [NSBundle bundleForClass:[SVProgressHUD class]]; - NSURL *url = [bundle URLForResource:@"SVProgressHUD" withExtension:@"bundle"]; - NSBundle *imageBundle = [NSBundle bundleWithURL:url]; - - UIImage* infoImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"info" ofType:@"png"]]; - UIImage* successImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"success" ofType:@"png"]]; - UIImage* errorImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"error" ofType:@"png"]]; - - if ([[UIImage class] instancesRespondToSelector:@selector(imageWithRenderingMode:)]) { - _infoImage = [infoImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - _successImage = [successImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - _errorImage = [errorImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - } else { - _infoImage = infoImage; - _successImage = successImage; - _errorImage = errorImage; - } - - _ringThickness = 2.0f; - _ringRadius = 18.0f; - _ringNoTextRadius = 24.0f; - - _cornerRadius = 14.0f; - - _minimumDismissTimeInterval = 5.0; - - _fadeInAnimationDuration = SVProgressHUDDefaultAnimationDuration; - _fadeOutAnimationDuration = SVProgressHUDDefaultAnimationDuration; - - // Accessibility support - self.accessibilityIdentifier = @"SVProgressHUD"; - self.accessibilityLabel = @"SVProgressHUD"; - self.isAccessibilityElement = YES; - - _isInitializing = NO; - } - return self; -} - -- (void)updateHUDFrame { - // For the beginning use default values, these - // might get update if string is too large etc. - CGFloat hudWidth = 100.0f; - CGFloat hudHeight = 100.0f; - CGFloat stringHeightBuffer = 20.0f; - CGFloat stringAndContentHeightBuffer = 80.0f; - CGRect labelRect = CGRectZero; - - // Check if an image or progress ring is displayed - BOOL imageUsed = (self.imageView.image) && !(self.imageView.hidden); - BOOL progressUsed = self.imageView.hidden; - - // Calculate size of string and update HUD size - NSString *string = self.statusLabel.text; - if(string) { - CGSize constraintSize = CGSizeMake(200.0f, 300.0f); - CGRect stringRect; - if([string respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) { - stringRect = [string boundingRectWithSize:constraintSize - options:(NSStringDrawingOptions)(NSStringDrawingUsesFontLeading|NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin) - attributes:@{NSFontAttributeName: self.statusLabel.font} - context:NULL]; - } else { - CGSize stringSize; - if([string respondsToSelector:@selector(sizeWithAttributes:)]) { - stringSize = [string sizeWithAttributes:@{NSFontAttributeName:[UIFont fontWithName:self.statusLabel.font.fontName size:self.statusLabel.font.pointSize]}]; - } else { -#if TARGET_OS_IOS -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" - stringSize = [string sizeWithFont:self.statusLabel.font constrainedToSize:CGSizeMake(200.0f, 300.0f)]; -#pragma clang diagnostic pop -#endif - } - stringRect = CGRectMake(0.0f, 0.0f, stringSize.width, stringSize.height); - } - - CGFloat stringWidth = stringRect.size.width; - CGFloat stringHeight = ceilf(CGRectGetHeight(stringRect)); - - if(imageUsed || progressUsed) { - hudHeight = stringAndContentHeightBuffer + stringHeight; - } else { - hudHeight = stringHeightBuffer + stringHeight; - } - if(stringWidth > hudWidth) { - hudWidth = ceilf(stringWidth/2)*2; - } - CGFloat labelRectY = (imageUsed || progressUsed) ? 68.0f : 9.0f; - if(hudHeight > 100.0f) { - labelRect = CGRectMake(12.0f, labelRectY, hudWidth, stringHeight); - hudWidth += 24.0f; - } else { - hudWidth += 24.0f; - labelRect = CGRectMake(0.0f, labelRectY, hudWidth, stringHeight); - } - } - - // Update values on subviews - self.hudView.bounds = CGRectMake(0.0f, 0.0f, MAX(self.minimumSize.width, hudWidth), MAX(self.minimumSize.height, hudHeight)); - labelRect.size.width += MAX(0, self.minimumSize.width - hudWidth); - [self updateBlurBounds]; - - if(string) { - self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, 36.0f); - } else { - self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, CGRectGetHeight(self.hudView.bounds)/2); - } - - self.statusLabel.hidden = NO; - self.statusLabel.frame = labelRect; - - // Animate value update - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - - if(string) { - if(self.defaultAnimationType == SVProgressHUDAnimationTypeFlat) { - SVIndefiniteAnimatedView *indefiniteAnimationView = (SVIndefiniteAnimatedView*)self.indefiniteAnimatedView; - indefiniteAnimationView.radius = self.ringRadius; - [indefiniteAnimationView sizeToFit]; - } - - CGPoint center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), 36.0f); - self.indefiniteAnimatedView.center = center; - - if(self.progress != SVProgressHUDUndefinedProgress) { - self.backgroundRingView.center = self.ringView.center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), 36.0f); - } - } else { - if(self.defaultAnimationType == SVProgressHUDAnimationTypeFlat) { - SVIndefiniteAnimatedView *indefiniteAnimationView = (SVIndefiniteAnimatedView*)self.indefiniteAnimatedView; - indefiniteAnimationView.radius = self.ringNoTextRadius; - [indefiniteAnimationView sizeToFit]; - } - - CGPoint center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), CGRectGetHeight(self.hudView.bounds)/2); - self.indefiniteAnimatedView.center = center; - - if(self.progress != SVProgressHUDUndefinedProgress) { - self.backgroundRingView.center = self.ringView.center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), CGRectGetHeight(self.hudView.bounds)/2); - } - } - - [CATransaction commit]; -} - -- (void)updateMask { - if(self.backgroundLayer) { - [self.backgroundLayer removeFromSuperlayer]; - self.backgroundLayer = nil; - } - switch (self.defaultMaskType) { - case SVProgressHUDMaskTypeCustom: - case SVProgressHUDMaskTypeBlack:{ - - self.backgroundLayer = [CALayer layer]; - self.backgroundLayer.frame = self.bounds; - self.backgroundLayer.backgroundColor = self.defaultMaskType == SVProgressHUDMaskTypeCustom ? self.backgroundLayerColor.CGColor : [UIColor colorWithWhite:0 alpha:0.4].CGColor; - [self.backgroundLayer setNeedsDisplay]; - - [self.layer insertSublayer:self.backgroundLayer atIndex:0]; - break; - } - - case SVProgressHUDMaskTypeGradient:{ - SVRadialGradientLayer *layer = [SVRadialGradientLayer layer]; - self.backgroundLayer = layer; - self.backgroundLayer.frame = self.bounds; - CGPoint gradientCenter = self.center; - gradientCenter.y = (self.bounds.size.height - self.visibleKeyboardHeight)/2; - layer.gradientCenter = gradientCenter; - [self.backgroundLayer setNeedsDisplay]; - - [self.layer insertSublayer:self.backgroundLayer atIndex:0]; - break; - } - default: - break; - } -} - -- (void)updateBlurBounds { -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 - if(NSClassFromString(@"UIBlurEffect") && self.defaultStyle != SVProgressHUDStyleCustom) { - // Remove background color, else the effect would not work - self.hudView.backgroundColor = [UIColor clearColor]; - - // Remove any old instances of UIVisualEffectViews - for (UIView *subview in self.hudView.subviews) { - if([subview isKindOfClass:[UIVisualEffectView class]]) { - [subview removeFromSuperview]; - } - } - - if(self.backgroundColor != [UIColor clearColor]) { - // Create blur effect - UIBlurEffectStyle blurEffectStyle = self.defaultStyle == SVProgressHUDStyleDark ? UIBlurEffectStyleDark : UIBlurEffectStyleLight; - UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:blurEffectStyle]; - UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; - blurEffectView.autoresizingMask = self.hudView.autoresizingMask; - blurEffectView.frame = self.hudView.bounds; - - // Add vibrancy to the blur effect to make it more vivid - UIVibrancyEffect *vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:blurEffect]; - UIVisualEffectView *vibrancyEffectView = [[UIVisualEffectView alloc] initWithEffect:vibrancyEffect]; - vibrancyEffectView.autoresizingMask = blurEffectView.autoresizingMask; - vibrancyEffectView.bounds = blurEffectView.bounds; - [blurEffectView.contentView addSubview:vibrancyEffectView]; - - [self.hudView insertSubview:blurEffectView atIndex:0]; - } - } -#endif -} - -#if TARGET_OS_IOS -- (void)updateMotionEffectForOrientation:(UIInterfaceOrientation)orientation { - UIInterpolatingMotionEffectType xMotionEffectType = UIInterfaceOrientationIsPortrait(orientation) ? UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis : UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis; - UIInterpolatingMotionEffectType yMotionEffectType = UIInterfaceOrientationIsPortrait(orientation) ? UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis : UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis; - [self updateMotionEffectForXMotionEffectType:xMotionEffectType yMotionEffectType:yMotionEffectType]; -} -#endif - -- (void)updateMotionEffectForXMotionEffectType:(UIInterpolatingMotionEffectType)xMotionEffectType yMotionEffectType:(UIInterpolatingMotionEffectType)yMotionEffectType { - if([self.hudView respondsToSelector:@selector(addMotionEffect:)]) { - UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:xMotionEffectType]; - effectX.minimumRelativeValue = @(-SVProgressHUDParallaxDepthPoints); - effectX.maximumRelativeValue = @(SVProgressHUDParallaxDepthPoints); - - UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:yMotionEffectType]; - effectY.minimumRelativeValue = @(-SVProgressHUDParallaxDepthPoints); - effectY.maximumRelativeValue = @(SVProgressHUDParallaxDepthPoints); - - UIMotionEffectGroup *effectGroup = [[UIMotionEffectGroup alloc] init]; - effectGroup.motionEffects = @[effectX, effectY]; - - // Clear old motion effect, then add new motion effects - self.hudView.motionEffects = @[]; - [self.hudView addMotionEffect:effectGroup]; - } -} - -- (void)updateViewHierachy { - // Add the overlay (e.g. black, gradient) to the application window if necessary - if(!self.overlayView.superview) { -#if !defined(SV_APP_EXTENSIONS) - // Default case: iterate over UIApplication windows - NSEnumerator *frontToBackWindows = [UIApplication.sharedApplication.windows reverseObjectEnumerator]; - for (UIWindow *window in frontToBackWindows) { - BOOL windowOnMainScreen = window.screen == UIScreen.mainScreen; - BOOL windowIsVisible = !window.hidden && window.alpha > 0; - BOOL windowLevelNormal = window.windowLevel == UIWindowLevelNormal; - - if(windowOnMainScreen && windowIsVisible && windowLevelNormal) { - [window addSubview:self.overlayView]; - break; - } - } -#else - // If SVProgressHUD ist used inside an app extension add it to the given view - if(self.viewForExtension) { - [self.viewForExtension addSubview:self.overlayView]; - } -#endif - } else { - // The HUD is already on screen, but maybot not in front. Therefore - // ensure that overlay will be on top of rootViewController (which may - // be changed during runtime). - [self.overlayView.superview bringSubviewToFront:self.overlayView]; - } - - - // Add self to the overlay view - if(!self.superview){ - [self.overlayView addSubview:self]; - } - if(!self.hudView.superview) { - [self addSubview:self.hudView]; - } -} - -- (void)setStatus:(NSString*)status { - self.statusLabel.text = status; - [self updateHUDFrame]; -} - -- (void)setFadeOutTimer:(NSTimer*)timer { - if(_fadeOutTimer) { - [_fadeOutTimer invalidate], _fadeOutTimer = nil; - } - if(timer) { - _fadeOutTimer = timer; - } -} - - -#pragma mark - Notifications and their handling - -- (void)registerNotifications { -#if TARGET_OS_IOS - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(positionHUD:) - name:UIApplicationDidChangeStatusBarOrientationNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(positionHUD:) - name:UIKeyboardWillHideNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(positionHUD:) - name:UIKeyboardDidHideNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(positionHUD:) - name:UIKeyboardWillShowNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(positionHUD:) - name:UIKeyboardDidShowNotification - object:nil]; -#endif - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(positionHUD:) - name:UIApplicationDidBecomeActiveNotification - object:nil]; -} - -- (NSDictionary*)notificationUserInfo{ - return (self.statusLabel.text ? @{SVProgressHUDStatusUserInfoKey : self.statusLabel.text} : nil); -} - -- (void)positionHUD:(NSNotification*)notification { - CGFloat keyboardHeight = 0.0f; - double animationDuration = 0.0; - -#if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS - self.frame = [[[UIApplication sharedApplication] delegate] window].bounds; - UIInterfaceOrientation orientation = UIApplication.sharedApplication.statusBarOrientation; -#elif !defined(SV_APP_EXTENSIONS) - self.frame = [UIApplication sharedApplication].keyWindow.bounds; -#else - if (self.viewForExtension) { - self.frame = self.viewForExtension.frame; - } else { - self.frame = UIScreen.mainScreen.bounds; - } - UIInterfaceOrientation orientation = CGRectGetWidth(self.frame) > CGRectGetHeight(self.frame) ? UIInterfaceOrientationLandscapeLeft : UIInterfaceOrientationPortrait; -#endif - - // no transforms applied to window in iOS 8, but only if compiled with iOS 8 sdk as base sdk, otherwise system supports old rotation logic. - BOOL ignoreOrientation = NO; -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 - if([[NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion)]) { - ignoreOrientation = YES; - } -#endif - -#if TARGET_OS_IOS - // Get keyboardHeight in regards to current state - if(notification) { - NSDictionary* keyboardInfo = [notification userInfo]; - CGRect keyboardFrame = [keyboardInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue]; - animationDuration = [keyboardInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; - - if(notification.name == UIKeyboardWillShowNotification || notification.name == UIKeyboardDidShowNotification) { - keyboardHeight = CGRectGetWidth(keyboardFrame); - - if(ignoreOrientation || UIInterfaceOrientationIsPortrait(orientation)) { - keyboardHeight = CGRectGetHeight(keyboardFrame); - } - } - } else { - keyboardHeight = self.visibleKeyboardHeight; - } -#endif - - // Get the currently active frame of the display (depends on orientation) - CGRect orientationFrame = self.bounds; - -#if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS - CGRect statusBarFrame = UIApplication.sharedApplication.statusBarFrame; -#else - CGRect statusBarFrame = CGRectZero; -#endif - -#if TARGET_OS_IOS - if(!ignoreOrientation && UIInterfaceOrientationIsLandscape(orientation)) { - float temp = CGRectGetWidth(orientationFrame); - orientationFrame.size.width = CGRectGetHeight(orientationFrame); - orientationFrame.size.height = temp; - - temp = CGRectGetWidth(statusBarFrame); - statusBarFrame.size.width = CGRectGetHeight(statusBarFrame); - statusBarFrame.size.height = temp; - } - - // Update the motion effects in regards to orientation - [self updateMotionEffectForOrientation:orientation]; -#else - [self updateMotionEffectForXMotionEffectType:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis yMotionEffectType:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; -#endif - - // Calculate available height for display - CGFloat activeHeight = CGRectGetHeight(orientationFrame); - if(keyboardHeight > 0) { - activeHeight += CGRectGetHeight(statusBarFrame)*2; - } - activeHeight -= keyboardHeight; - - CGFloat posX = CGRectGetWidth(orientationFrame)/2.0f; - CGFloat posY = floorf(activeHeight*0.45f); - - CGFloat rotateAngle = 0.0; - CGPoint newCenter = CGPointMake(posX, posY); - - // Update posX and posY in regards to orientation -#if TARGET_OS_IOS - if(!ignoreOrientation) { - switch (orientation) { - case UIInterfaceOrientationPortraitUpsideDown: - rotateAngle = (CGFloat) M_PI; - newCenter = CGPointMake(posX, CGRectGetHeight(orientationFrame)-posY); - break; - case UIInterfaceOrientationLandscapeLeft: - rotateAngle = (CGFloat) (-M_PI/2.0f); - newCenter = CGPointMake(posY, posX); - break; - case UIInterfaceOrientationLandscapeRight: - rotateAngle = (CGFloat) (M_PI/2.0f); - newCenter = CGPointMake(CGRectGetHeight(orientationFrame)-posY, posX); - break; - default: // Same as UIInterfaceOrientationPortrait - rotateAngle = 0.0f; - newCenter = CGPointMake(posX, posY); - break; - } - } -#endif - - if(notification) { - // Animate update if notification was present - __weak SVProgressHUD *weakSelf = self; - [UIView animateWithDuration:animationDuration - delay:0 - options:UIViewAnimationOptionAllowUserInteraction - animations:^{ - __strong SVProgressHUD *strongSelf = weakSelf; - if(strongSelf) { - [strongSelf moveToPoint:newCenter rotateAngle:rotateAngle]; - [strongSelf.hudView setNeedsDisplay]; - } - } completion:NULL]; - } else { - [self moveToPoint:newCenter rotateAngle:rotateAngle]; - [self.hudView setNeedsDisplay]; - } - - [self updateMask]; -} - -- (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle { - self.hudView.transform = CGAffineTransformMakeRotation(angle); - self.hudView.center = CGPointMake(newCenter.x + self.offsetFromCenter.horizontal, newCenter.y + self.offsetFromCenter.vertical); -} - - -#pragma mark - Event handling - -- (void)overlayViewDidReceiveTouchEvent:(id)sender forEvent:(UIEvent*)event { - [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidReceiveTouchEventNotification - object:self - userInfo:[self notificationUserInfo]]; - - UITouch *touch = event.allTouches.anyObject; - CGPoint touchLocation = [touch locationInView:self]; - - if(CGRectContainsPoint(self.hudView.frame, touchLocation)) { - [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidTouchDownInsideNotification - object:self - userInfo:[self notificationUserInfo]]; - } -} - - -#pragma mark - Master show/dismiss methods - -- (void)showProgress:(float)progress status:(NSString*)status { - __weak SVProgressHUD *weakSelf = self; - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - __strong SVProgressHUD *strongSelf = weakSelf; - if(strongSelf){ - // Update / Check view hierachy to ensure the HUD is visible - [strongSelf updateViewHierachy]; - - // Reset imageView and fadeout timer if an image is currently displayed - strongSelf.imageView.hidden = YES; - strongSelf.imageView.image = nil; - - if(strongSelf.fadeOutTimer) { - strongSelf.activityCount = 0; - } - strongSelf.fadeOutTimer = nil; - - // Update text and set progress to the given value - strongSelf.statusLabel.text = status; - strongSelf.progress = progress; - - // Choose the "right" indicator depending on the progress - if(progress >= 0) { - // Cancel the indefiniteAnimatedView, then show the ringLayer - [strongSelf cancelIndefiniteAnimatedViewAnimation]; - - // Add ring to HUD and set progress - [strongSelf.hudView addSubview:strongSelf.ringView]; - [strongSelf.hudView addSubview:strongSelf.backgroundRingView]; - strongSelf.ringView.strokeEnd = progress; - - // Updat the activity count - if(progress == 0) { - strongSelf.activityCount++; - } - } else { - // Cancel the ringLayer animation, then show the indefiniteAnimatedView - [strongSelf cancelRingLayerAnimation]; - - // Add indefiniteAnimatedView to HUD - [strongSelf.hudView addSubview:strongSelf.indefiniteAnimatedView]; - if([strongSelf.indefiniteAnimatedView respondsToSelector:@selector(startAnimating)]) { - [(id)strongSelf.indefiniteAnimatedView startAnimating]; - } - - // Update the activity count - strongSelf.activityCount++; - } - - // Show - [strongSelf showStatus:status]; - } - }]; -} - -- (void)showImage:(UIImage*)image status:(NSString*)status duration:(NSTimeInterval)duration { - __weak SVProgressHUD *weakSelf = self; - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - __strong SVProgressHUD *strongSelf = weakSelf; - if(strongSelf){ - // Update / Check view hierachy to ensure the HUD is visible - [strongSelf updateViewHierachy]; - - // Reset progress and cancel any running animation - strongSelf.progress = SVProgressHUDUndefinedProgress; - [strongSelf cancelRingLayerAnimation]; - [strongSelf cancelIndefiniteAnimatedViewAnimation]; - - // Update imageView - UIColor *tintColor = strongSelf.foregroundColorForStyle; - UIImage *tintedImage = image; - if([strongSelf.imageView respondsToSelector:@selector(setTintColor:)]) { - if (tintedImage.renderingMode != UIImageRenderingModeAlwaysTemplate) { - tintedImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - } - strongSelf.imageView.tintColor = tintColor; - } else { - tintedImage = [strongSelf image:image withTintColor:tintColor]; - } - strongSelf.imageView.image = tintedImage; - strongSelf.imageView.hidden = NO; - - // Update text - strongSelf.statusLabel.text = status; - - // Show - [strongSelf showStatus:status]; - - // An image will dismissed automatically. Therefore we start a timer - // which then will call dismiss after the predefined duration - strongSelf.fadeOutTimer = [NSTimer timerWithTimeInterval:duration target:strongSelf selector:@selector(dismiss) userInfo:nil repeats:NO]; - [[NSRunLoop mainRunLoop] addTimer:strongSelf.fadeOutTimer forMode:NSRunLoopCommonModes]; - } - }]; -} - -- (void)showStatus:(NSString*)status { - // Update the HUDs frame to the new content and position HUD - [self updateHUDFrame]; - [self positionHUD:nil]; - - // Update accesibilty as well as user interaction - if(self.defaultMaskType != SVProgressHUDMaskTypeNone) { - self.overlayView.userInteractionEnabled = YES; - self.accessibilityLabel = status; - self.isAccessibilityElement = YES; - } else { - self.overlayView.userInteractionEnabled = NO; - self.hudView.accessibilityLabel = status; - self.hudView.isAccessibilityElement = YES; - } - - // Show overlay - self.overlayView.backgroundColor = [UIColor clearColor]; - - // Show if not already visible (depending on alpha) - if(self.alpha != 1.0f || self.hudView.alpha != 1.0f) { - // Post notification to inform user - [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillAppearNotification - object:self - userInfo:[self notificationUserInfo]]; - - // Zoom HUD a little to make a nice appear / pop up animation - self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1.3, 1.3); - - // Set initial values to handle iOS 7 (and above) UIToolbar which not answers well to hierarchy opacity change - self.alpha = 0.0f; - self.hudView.alpha = 0.0f; - - // Define blocks - __weak SVProgressHUD *weakSelf = self; - - __block void (^animationsBlock)(void) = ^{ - __strong SVProgressHUD *strongSelf = weakSelf; - if(strongSelf) { - // Shrink HUD to finish pop up animation - strongSelf.hudView.transform = CGAffineTransformScale(strongSelf.hudView.transform, 1/1.3f, 1/1.3f); - strongSelf.alpha = 1.0f; - strongSelf.hudView.alpha = 1.0f; - } - }; - - __block void (^completionBlock)(void) = ^{ - __strong SVProgressHUD *strongSelf = weakSelf; - if(strongSelf) { - /// Register observer <=> we now have to handle orientation changes etc. - [strongSelf registerNotifications]; - - // Post notification to inform user - [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidAppearNotification - object:strongSelf - userInfo:[strongSelf notificationUserInfo]]; - } - - // Update accesibilty - UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil); - UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, status); - }; - - if (self.fadeInAnimationDuration > 0) { - // Animate appearance - [UIView animateWithDuration:self.fadeInAnimationDuration - delay:0 - options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState) - animations:^{ - animationsBlock(); - } completion:^(BOOL finished) { - completionBlock(); - }]; - } else { - animationsBlock(); - completionBlock(); - } - - // Inform iOS to redraw the view hierachy - [self setNeedsDisplay]; - } -} - -- (void)dismiss { - [self dismissWithDelay:0]; -} - -- (void)dismissWithDelay:(NSTimeInterval)delay { - __weak SVProgressHUD *weakSelf = self; - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - __strong SVProgressHUD *strongSelf = weakSelf; - if(strongSelf){ - // Dismiss if visible (depending on alpha) - if(strongSelf.alpha != 0.0f || strongSelf.hudView.alpha != 0.0f){ - // Post notification to inform user - [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillDisappearNotification - object:nil - userInfo:[strongSelf notificationUserInfo]]; - - // Reset activitiy count - strongSelf.activityCount = 0; - - // Define blocks - __block void (^animationsBlock)(void) = ^{ - strongSelf.hudView.transform = CGAffineTransformScale(strongSelf.hudView.transform, 0.8f, 0.8f); - strongSelf.alpha = 0.0f; - strongSelf.hudView.alpha = 0.0f; - }; - - __block void (^completionBlock)(void) = ^{ - // Clean up view hierachy (overlays) - [strongSelf.overlayView removeFromSuperview]; - [strongSelf.hudView removeFromSuperview]; - [strongSelf removeFromSuperview]; - - // Reset progress and cancel any running animation - strongSelf.progress = SVProgressHUDUndefinedProgress; - [strongSelf cancelRingLayerAnimation]; - [strongSelf cancelIndefiniteAnimatedViewAnimation]; - - // Remove observer <=> we do not have to handle orientation changes etc. - [[NSNotificationCenter defaultCenter] removeObserver:strongSelf]; - - // Post notification to inform user - [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidDisappearNotification - object:strongSelf - userInfo:[strongSelf notificationUserInfo]]; - - // Tell the rootViewController to update the StatusBar appearance -#if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS - UIViewController *rootController = [[UIApplication sharedApplication] keyWindow].rootViewController; - if([rootController respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) { - [rootController setNeedsStatusBarAppearanceUpdate]; - } -#endif - // Update accesibilty - UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil); - }; - - if (strongSelf.fadeOutAnimationDuration > 0) { - // Animate appearance - [UIView animateWithDuration:strongSelf.fadeOutAnimationDuration - delay:delay - options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) - animations:^{ - animationsBlock(); - } completion:^(BOOL finished) { - completionBlock(); - }]; - } else { - animationsBlock(); - completionBlock(); - } - - // Inform iOS to redraw the view hierachy - [strongSelf setNeedsDisplay]; - } - } - }]; -} - - -#pragma mark - Ring progress animation - -- (UIView*)indefiniteAnimatedView { - // Get the correct spinner for defaultAnimationType - if(self.defaultAnimationType == SVProgressHUDAnimationTypeFlat){ - // Check if spinner exists and is an object of different class - if(_indefiniteAnimatedView && ![_indefiniteAnimatedView isKindOfClass:[SVIndefiniteAnimatedView class]]){ - [_indefiniteAnimatedView removeFromSuperview]; - _indefiniteAnimatedView = nil; - } - - if(!_indefiniteAnimatedView){ - _indefiniteAnimatedView = [[SVIndefiniteAnimatedView alloc] initWithFrame:CGRectZero]; - } - - // Update styling - SVIndefiniteAnimatedView *indefiniteAnimatedView = (SVIndefiniteAnimatedView*)_indefiniteAnimatedView; - indefiniteAnimatedView.strokeColor = self.foregroundColorForStyle; - indefiniteAnimatedView.strokeThickness = self.ringThickness; - indefiniteAnimatedView.radius = self.statusLabel.text ? self.ringRadius : self.ringNoTextRadius; - } else { - // Check if spinner exists and is an object of different class - if(_indefiniteAnimatedView && ![_indefiniteAnimatedView isKindOfClass:[UIActivityIndicatorView class]]){ - [_indefiniteAnimatedView removeFromSuperview]; - _indefiniteAnimatedView = nil; - } - - if(!_indefiniteAnimatedView){ - _indefiniteAnimatedView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; - } - - // Update styling - UIActivityIndicatorView *activityIndicatorView = (UIActivityIndicatorView*)_indefiniteAnimatedView; - activityIndicatorView.color = self.foregroundColorForStyle; - } - [_indefiniteAnimatedView sizeToFit]; - - return _indefiniteAnimatedView; -} - -- (SVProgressAnimatedView*)ringView { - if(!_ringView) { - _ringView = [[SVProgressAnimatedView alloc] initWithFrame:CGRectZero]; - } - - // Update styling - _ringView.strokeColor = self.foregroundColorForStyle; - _ringView.strokeThickness = self.ringThickness; - _ringView.radius = self.statusLabel.text ? self.ringRadius : self.ringNoTextRadius; - - return _ringView; -} - -- (SVProgressAnimatedView*)backgroundRingView { - if(!_backgroundRingView) { - _backgroundRingView = [[SVProgressAnimatedView alloc] initWithFrame:CGRectZero]; - _backgroundRingView.strokeEnd = 1.0f; - } - - // Update styling - _backgroundRingView.strokeColor = [self.foregroundColorForStyle colorWithAlphaComponent:0.1f]; - _backgroundRingView.strokeThickness = self.ringThickness; - _backgroundRingView.radius = self.statusLabel.text ? self.ringRadius : self.ringNoTextRadius; - - return _backgroundRingView; -} - -- (void)cancelRingLayerAnimation { - // Animate value update, stop animation - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - - [self.hudView.layer removeAllAnimations]; - self.ringView.strokeEnd = 0.0f; - - [CATransaction commit]; - - // Remove from view - [self.ringView removeFromSuperview]; - [self.backgroundRingView removeFromSuperview]; -} - -- (void)cancelIndefiniteAnimatedViewAnimation { - // Stop animation - if([self.indefiniteAnimatedView respondsToSelector:@selector(stopAnimating)]) { - [(id)self.indefiniteAnimatedView stopAnimating]; - } - // Remove from view - [self.indefiniteAnimatedView removeFromSuperview]; -} - - -#pragma mark - Utilities - -+ (BOOL)isVisible { - return ([self sharedView].alpha > 0); -} - - -#pragma mark - Getters - -+ (NSTimeInterval)displayDurationForString:(NSString*)string { - return MAX((float)string.length * 0.06 + 0.5, [self sharedView].minimumDismissTimeInterval); -} - -- (UIColor*)foregroundColorForStyle { - if(self.defaultStyle == SVProgressHUDStyleLight) { - return [UIColor blackColor]; - } else if(self.defaultStyle == SVProgressHUDStyleDark) { - return [UIColor whiteColor]; - } else { - return self.foregroundColor; - } -} - -- (UIColor*)backgroundColorForStyle { - if(self.defaultStyle == SVProgressHUDStyleLight) { - return [UIColor whiteColor]; - } else if(self.defaultStyle == SVProgressHUDStyleDark) { - return [UIColor blackColor]; - } else { - return self.backgroundColor; - } -} - -- (UIImage*)image:(UIImage*)image withTintColor:(UIColor*)color { - CGRect rect = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height); - UIGraphicsBeginImageContextWithOptions(rect.size, NO, image.scale); - CGContextRef c = UIGraphicsGetCurrentContext(); - [image drawInRect:rect]; - CGContextSetFillColorWithColor(c, [color CGColor]); - CGContextSetBlendMode(c, kCGBlendModeSourceAtop); - CGContextFillRect(c, rect); - UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return tintedImage; -} - -- (BOOL)isClear { // used for iOS 7 and above - return (self.defaultMaskType == SVProgressHUDMaskTypeClear || self.defaultMaskType == SVProgressHUDMaskTypeNone); -} - -- (UIControl*)overlayView { - if(!_overlayView) { -#if !defined(SV_APP_EXTENSIONS) - CGRect windowBounds = [[[UIApplication sharedApplication] delegate] window].bounds; - _overlayView = [[UIControl alloc] initWithFrame:windowBounds]; -#else - _overlayView = [[UIControl alloc] initWithFrame:[UIScreen mainScreen].bounds]; -#endif - _overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - _overlayView.backgroundColor = [UIColor clearColor]; - [_overlayView addTarget:self action:@selector(overlayViewDidReceiveTouchEvent:forEvent:) forControlEvents:UIControlEventTouchDown]; - } - return _overlayView; -} - -- (UIView*)hudView { - if(!_hudView) { - _hudView = [[UIView alloc] initWithFrame:CGRectZero]; - _hudView.layer.masksToBounds = YES; - _hudView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin; - } - - // Update styling - _hudView.layer.cornerRadius = self.cornerRadius; - _hudView.backgroundColor = self.backgroundColorForStyle; - - return _hudView; -} - -- (UILabel*)statusLabel { - if(!_statusLabel) { - _statusLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - _statusLabel.backgroundColor = [UIColor clearColor]; - _statusLabel.adjustsFontSizeToFitWidth = YES; - _statusLabel.textAlignment = NSTextAlignmentCenter; - _statusLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; - _statusLabel.numberOfLines = 0; - } - if(!_statusLabel.superview) { - [self.hudView addSubview:_statusLabel]; - } - - // Update styling - _statusLabel.textColor = self.foregroundColorForStyle; - _statusLabel.font = self.font; - - return _statusLabel; -} - -- (UIImageView*)imageView { - if(!_imageView) { - _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 28.0f, 28.0f)]; - } - if(!_imageView.superview) { - [self.hudView addSubview:_imageView]; - } - return _imageView; -} - -- (CGFloat)visibleKeyboardHeight { -#if !defined(SV_APP_EXTENSIONS) - UIWindow *keyboardWindow = nil; - for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) { - if(![[testWindow class] isEqual:[UIWindow class]]) { - keyboardWindow = testWindow; - break; - } - } - - for (__strong UIView *possibleKeyboard in [keyboardWindow subviews]) { - if([possibleKeyboard isKindOfClass:NSClassFromString(@"UIPeripheralHostView")] || [possibleKeyboard isKindOfClass:NSClassFromString(@"UIKeyboard")]) { - return CGRectGetHeight(possibleKeyboard.bounds); - } else if([possibleKeyboard isKindOfClass:NSClassFromString(@"UIInputSetContainerView")]) { - for (__strong UIView *possibleKeyboardSubview in [possibleKeyboard subviews]) { - if([possibleKeyboardSubview isKindOfClass:NSClassFromString(@"UIInputSetHostView")]) { - return CGRectGetHeight(possibleKeyboardSubview.bounds); - } - } - } - } -#endif - return 0; -} - - -#pragma mark - UIAppearance Setters - -- (void)setDefaultStyle:(SVProgressHUDStyle)style { - if (!_isInitializing) _defaultStyle = style; -} - -- (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType { - if (!_isInitializing) _defaultMaskType = maskType; -} - -- (void)setDefaultAnimationType:(SVProgressHUDAnimationType)animationType { - if (!_isInitializing) _defaultAnimationType = animationType; -} - -- (void)setMinimumSize:(CGSize)minimumSize { - if (!_isInitializing) _minimumSize = minimumSize; -} - -- (void)setRingThickness:(CGFloat)ringThickness { - if (!_isInitializing) _ringThickness = ringThickness; -} - -- (void)setRingRadius:(CGFloat)ringRadius { - if (!_isInitializing) _ringRadius = ringRadius; -} - -- (void)setRingNoTextRadius:(CGFloat)ringNoTextRadius { - if (!_isInitializing) _ringNoTextRadius = ringNoTextRadius; -} - -- (void)setCornerRadius:(CGFloat)cornerRadius { - if (!_isInitializing) _cornerRadius = cornerRadius; -} - -- (void)setFont:(UIFont*)font { - if (!_isInitializing) _font = font; -} - -- (void)setForegroundColor:(UIColor*)color { - if (!_isInitializing) _foregroundColor = color; -} - -- (void)setBackgroundColor:(UIColor*)color { - if (!_isInitializing) _backgroundColor = color; -} - -- (void)setBackgroundLayerColor:(UIColor*)color { - if (!_isInitializing) _backgroundLayerColor = color; -} - -- (void)setInfoImage:(UIImage*)image { - if (!_isInitializing) _infoImage = image; -} - -- (void)setSuccessImage:(UIImage*)image { - if (!_isInitializing) _successImage = image; -} - -- (void)setErrorImage:(UIImage*)image { - if (!_isInitializing) _errorImage = image; -} - -- (void)setViewForExtension:(UIView*)view { - if (!_isInitializing) _viewForExtension = view; -} - -- (void)setOffsetFromCenter:(UIOffset)offset { - if (!_isInitializing) _offsetFromCenter = offset; -} - -- (void)setMinimumDismissTimeInterval:(NSTimeInterval)minimumDismissTimeInterval { - if (!_isInitializing) _minimumDismissTimeInterval = minimumDismissTimeInterval; -} - -- (void)setFadeInAnimationDuration:(NSTimeInterval)duration { - if (!_isInitializing) _fadeInAnimationDuration = duration; -} - -- (void)setFadeOutAnimationDuration:(NSTimeInterval)duration { - if (!_isInitializing) _fadeOutAnimationDuration = duration; -} - -@end - diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.h b/Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.h deleted file mode 100644 index 634449e..0000000 --- a/Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// SVRadialGradientLayer.h -// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD -// -// Copyright (c) 2014-2016 Tobias Tiemerding. All rights reserved. -// - -#import - -@interface SVRadialGradientLayer : CALayer - -@property (nonatomic) CGPoint gradientCenter; - -@end diff --git a/Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.m b/Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.m deleted file mode 100644 index d6d5827..0000000 --- a/Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.m +++ /dev/null @@ -1,25 +0,0 @@ -// -// SVRadialGradientLayer.m -// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD -// -// Copyright (c) 2014-2016 Tobias Tiemerding. All rights reserved. -// - -#import "SVRadialGradientLayer.h" - -@implementation SVRadialGradientLayer - -- (void)drawInContext:(CGContextRef)context { - size_t locationsCount = 2; - CGFloat locations[2] = {0.0f, 1.0f}; - CGFloat colors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f}; - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, locationsCount); - CGColorSpaceRelease(colorSpace); - - float radius = MIN(self.bounds.size.width , self.bounds.size.height); - CGContextDrawRadialGradient (context, gradient, self.gradientCenter, 0, self.gradientCenter, radius, kCGGradientDrawsAfterEndLocation); - CGGradientRelease(gradient); -} - -@end diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking-dummy.m b/Pods/Target Support Files/AFNetworking/AFNetworking-dummy.m deleted file mode 100644 index 6a29cf8..0000000 --- a/Pods/Target Support Files/AFNetworking/AFNetworking-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_AFNetworking : NSObject -@end -@implementation PodsDummy_AFNetworking -@end diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking-prefix.pch b/Pods/Target Support Files/AFNetworking/AFNetworking-prefix.pch deleted file mode 100644 index b52cf0d..0000000 --- a/Pods/Target Support Files/AFNetworking/AFNetworking-prefix.pch +++ /dev/null @@ -1,15 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - -#ifndef TARGET_OS_IOS - #define TARGET_OS_IOS TARGET_OS_IPHONE -#endif - -#ifndef TARGET_OS_WATCH - #define TARGET_OS_WATCH 0 -#endif - -#ifndef TARGET_OS_TV - #define TARGET_OS_TV 0 -#endif diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking.xcconfig b/Pods/Target Support Files/AFNetworking/AFNetworking.xcconfig deleted file mode 100644 index 689a653..0000000 --- a/Pods/Target Support Files/AFNetworking/AFNetworking.xcconfig +++ /dev/null @@ -1,5 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/AFNetworking" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -OTHER_LDFLAGS = -framework "CoreGraphics" -framework "MobileCoreServices" -framework "Security" -framework "SystemConfiguration" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/DACircularProgress/DACircularProgress-dummy.m b/Pods/Target Support Files/DACircularProgress/DACircularProgress-dummy.m deleted file mode 100644 index 76cdc76..0000000 --- a/Pods/Target Support Files/DACircularProgress/DACircularProgress-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_DACircularProgress : NSObject -@end -@implementation PodsDummy_DACircularProgress -@end diff --git a/Pods/Target Support Files/DACircularProgress/DACircularProgress-prefix.pch b/Pods/Target Support Files/DACircularProgress/DACircularProgress-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Pods/Target Support Files/DACircularProgress/DACircularProgress-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Pods/Target Support Files/DACircularProgress/DACircularProgress.xcconfig b/Pods/Target Support Files/DACircularProgress/DACircularProgress.xcconfig deleted file mode 100644 index 632aa6c..0000000 --- a/Pods/Target Support Files/DACircularProgress/DACircularProgress.xcconfig +++ /dev/null @@ -1,5 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/DACircularProgress" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -OTHER_LDFLAGS = -framework "QuartzCore" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/DKNightVersion/DKNightVersion-dummy.m b/Pods/Target Support Files/DKNightVersion/DKNightVersion-dummy.m deleted file mode 100644 index 1b86951..0000000 --- a/Pods/Target Support Files/DKNightVersion/DKNightVersion-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_DKNightVersion : NSObject -@end -@implementation PodsDummy_DKNightVersion -@end diff --git a/Pods/Target Support Files/DKNightVersion/DKNightVersion-prefix.pch b/Pods/Target Support Files/DKNightVersion/DKNightVersion-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Pods/Target Support Files/DKNightVersion/DKNightVersion-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Pods/Target Support Files/DKNightVersion/DKNightVersion.xcconfig b/Pods/Target Support Files/DKNightVersion/DKNightVersion.xcconfig deleted file mode 100644 index da2cdb4..0000000 --- a/Pods/Target Support Files/DKNightVersion/DKNightVersion.xcconfig +++ /dev/null @@ -1,4 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/DKNightVersion" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/FMDB/FMDB-dummy.m b/Pods/Target Support Files/FMDB/FMDB-dummy.m deleted file mode 100644 index 20ea8f1..0000000 --- a/Pods/Target Support Files/FMDB/FMDB-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_FMDB : NSObject -@end -@implementation PodsDummy_FMDB -@end diff --git a/Pods/Target Support Files/FMDB/FMDB-prefix.pch b/Pods/Target Support Files/FMDB/FMDB-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Pods/Target Support Files/FMDB/FMDB-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Pods/Target Support Files/FMDB/FMDB.xcconfig b/Pods/Target Support Files/FMDB/FMDB.xcconfig deleted file mode 100644 index ba3a582..0000000 --- a/Pods/Target Support Files/FMDB/FMDB.xcconfig +++ /dev/null @@ -1,5 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/FMDB" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -OTHER_LDFLAGS = -l"sqlite3" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/MJExtension/MJExtension-dummy.m b/Pods/Target Support Files/MJExtension/MJExtension-dummy.m deleted file mode 100644 index 79c234e..0000000 --- a/Pods/Target Support Files/MJExtension/MJExtension-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_MJExtension : NSObject -@end -@implementation PodsDummy_MJExtension -@end diff --git a/Pods/Target Support Files/MJExtension/MJExtension-prefix.pch b/Pods/Target Support Files/MJExtension/MJExtension-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Pods/Target Support Files/MJExtension/MJExtension-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Pods/Target Support Files/MJExtension/MJExtension.xcconfig b/Pods/Target Support Files/MJExtension/MJExtension.xcconfig deleted file mode 100644 index 27658ee..0000000 --- a/Pods/Target Support Files/MJExtension/MJExtension.xcconfig +++ /dev/null @@ -1,4 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/MJExtension" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/MJRefresh/MJRefresh-dummy.m b/Pods/Target Support Files/MJRefresh/MJRefresh-dummy.m deleted file mode 100644 index d43259d..0000000 --- a/Pods/Target Support Files/MJRefresh/MJRefresh-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_MJRefresh : NSObject -@end -@implementation PodsDummy_MJRefresh -@end diff --git a/Pods/Target Support Files/MJRefresh/MJRefresh-prefix.pch b/Pods/Target Support Files/MJRefresh/MJRefresh-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Pods/Target Support Files/MJRefresh/MJRefresh-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Pods/Target Support Files/MJRefresh/MJRefresh.xcconfig b/Pods/Target Support Files/MJRefresh/MJRefresh.xcconfig deleted file mode 100644 index 5fc27bd..0000000 --- a/Pods/Target Support Files/MJRefresh/MJRefresh.xcconfig +++ /dev/null @@ -1,4 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/MJRefresh" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/NJKWebViewProgress/NJKWebViewProgress-dummy.m b/Pods/Target Support Files/NJKWebViewProgress/NJKWebViewProgress-dummy.m deleted file mode 100644 index d81e97c..0000000 --- a/Pods/Target Support Files/NJKWebViewProgress/NJKWebViewProgress-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_NJKWebViewProgress : NSObject -@end -@implementation PodsDummy_NJKWebViewProgress -@end diff --git a/Pods/Target Support Files/NJKWebViewProgress/NJKWebViewProgress-prefix.pch b/Pods/Target Support Files/NJKWebViewProgress/NJKWebViewProgress-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Pods/Target Support Files/NJKWebViewProgress/NJKWebViewProgress-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Pods/Target Support Files/NJKWebViewProgress/NJKWebViewProgress.xcconfig b/Pods/Target Support Files/NJKWebViewProgress/NJKWebViewProgress.xcconfig deleted file mode 100644 index 22e9c7f..0000000 --- a/Pods/Target Support Files/NJKWebViewProgress/NJKWebViewProgress.xcconfig +++ /dev/null @@ -1,4 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-acknowledgements.markdown b/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-acknowledgements.markdown deleted file mode 100644 index 2dbcb2a..0000000 --- a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-acknowledgements.markdown +++ /dev/null @@ -1,267 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## AFNetworking - -Copyright (c) 2011–2016 Alamofire Software Foundation (http://alamofire.org/) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - -## DACircularProgress - -# License - -## MIT License - -Copyright (c) 2013 Daniel Amitay (http://danielamitay.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -## DKNightVersion - -The MIT License (MIT) - -Copyright (c) 2015 Draveness - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - - -## FMDB - -If you are using FMDB in your project, I'd love to hear about it. Let Gus know -by sending an email to gus@flyingmeat.com. - -And if you happen to come across either Gus Mueller or Rob Ryan in a bar, you -might consider purchasing a drink of their choosing if FMDB has been useful to -you. - -Finally, and shortly, this is the MIT License. - -Copyright (c) 2008-2014 Flying Meat Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -## MJExtension - -Copyright (c) 2013-2015 MJExtension (https://github.com/CoderMJLee/MJExtension) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - -## MJRefresh - -Copyright (c) 2013-2015 MJRefresh (https://github.com/CoderMJLee/MJRefresh) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - -## NJKWebViewProgress - -The MIT License (MIT) - -Copyright (c) 2013 Satoshi Asano - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -## SDWebImage - -Copyright (c) 2009 Olivier Poitrey - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - -## SVProgressHUD - -Copyright (c) 2011-2016 Sam Vermette, Tobias Tiemerding and contributors. - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -A different license may apply to other resources included in this package, -including Freepik Icons. Please consult their -respective headers for the terms of their individual licenses. - - -## pop - -BSD License - -For Pop software - -Copyright (c) 2014, Facebook, Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Generated by CocoaPods - http://cocoapods.org diff --git a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-acknowledgements.plist b/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-acknowledgements.plist deleted file mode 100644 index 84f5d6b..0000000 --- a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-acknowledgements.plist +++ /dev/null @@ -1,333 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - Copyright (c) 2011–2016 Alamofire Software Foundation (http://alamofire.org/) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - Title - AFNetworking - Type - PSGroupSpecifier - - - FooterText - # License - -## MIT License - -Copyright (c) 2013 Daniel Amitay (http://danielamitay.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - Title - DACircularProgress - Type - PSGroupSpecifier - - - FooterText - The MIT License (MIT) - -Copyright (c) 2015 Draveness - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - - Title - DKNightVersion - Type - PSGroupSpecifier - - - FooterText - If you are using FMDB in your project, I'd love to hear about it. Let Gus know -by sending an email to gus@flyingmeat.com. - -And if you happen to come across either Gus Mueller or Rob Ryan in a bar, you -might consider purchasing a drink of their choosing if FMDB has been useful to -you. - -Finally, and shortly, this is the MIT License. - -Copyright (c) 2008-2014 Flying Meat Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - Title - FMDB - Type - PSGroupSpecifier - - - FooterText - Copyright (c) 2013-2015 MJExtension (https://github.com/CoderMJLee/MJExtension) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - Title - MJExtension - Type - PSGroupSpecifier - - - FooterText - Copyright (c) 2013-2015 MJRefresh (https://github.com/CoderMJLee/MJRefresh) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - Title - MJRefresh - Type - PSGroupSpecifier - - - FooterText - The MIT License (MIT) - -Copyright (c) 2013 Satoshi Asano - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - Title - NJKWebViewProgress - Type - PSGroupSpecifier - - - FooterText - Copyright (c) 2009 Olivier Poitrey <rs@dailymotion.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - Title - SDWebImage - Type - PSGroupSpecifier - - - FooterText - Copyright (c) 2011-2016 Sam Vermette, Tobias Tiemerding and contributors. - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -A different license may apply to other resources included in this package, -including Freepik Icons. Please consult their -respective headers for the terms of their individual licenses. - - Title - SVProgressHUD - Type - PSGroupSpecifier - - - FooterText - BSD License - -For Pop software - -Copyright (c) 2014, Facebook, Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Title - pop - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - http://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-dummy.m b/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-dummy.m deleted file mode 100644 index 6a4f450..0000000 --- a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_TTNews : NSObject -@end -@implementation PodsDummy_Pods_TTNews -@end diff --git a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-frameworks.sh b/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-frameworks.sh deleted file mode 100755 index 6f76344..0000000 --- a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-frameworks.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/sh -set -e - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" - -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink "${source}")" - fi - - # use filter instead of exclude so missing patterns dont' throw errors - echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identitiy - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" - /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" - fi -} - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - # Get architectures for current file - archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" - stripped="" - for arch in $archs; do - if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" || exit 1 - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi -} - diff --git a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-resources.sh b/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-resources.sh deleted file mode 100755 index 8506980..0000000 --- a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-resources.sh +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/sh -set -e - -mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - -RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt -> "$RESOURCES_TO_COPY" - -XCASSET_FILES=() - -realpath() { - DIRECTORY="$(cd "${1%/*}" && pwd)" - FILENAME="${1##*/}" - echo "$DIRECTORY/$FILENAME" -} - -install_resource() -{ - case $1 in - *.storyboard) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" - ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" - ;; - *.xib) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" - ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" - ;; - *.framework) - echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - ;; - *.xcdatamodel) - echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" - xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" - ;; - *.xcdatamodeld) - echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" - xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" - ;; - *.xcmappingmodel) - echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" - xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" - ;; - *.xcassets) - ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") - XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") - ;; - /*) - echo "$1" - echo "$1" >> "$RESOURCES_TO_COPY" - ;; - *) - echo "${PODS_ROOT}/$1" - echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" - ;; - esac -} -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_resource "DKNightVersion/DKNightVersion/ColorTable/DKColorTable.txt" - install_resource "MJRefresh/MJRefresh/MJRefresh.bundle" - install_resource "SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_resource "DKNightVersion/DKNightVersion/ColorTable/DKColorTable.txt" - install_resource "MJRefresh/MJRefresh/MJRefresh.bundle" - install_resource "SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle" -fi - -mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then - mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -fi -rm -f "$RESOURCES_TO_COPY" - -if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] -then - case "${TARGETED_DEVICE_FAMILY}" in - 1,2) - TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" - ;; - 1) - TARGET_DEVICE_ARGS="--target-device iphone" - ;; - 2) - TARGET_DEVICE_ARGS="--target-device ipad" - ;; - *) - TARGET_DEVICE_ARGS="--target-device mac" - ;; - esac - - # Find all other xcassets (this unfortunately includes those of path pods and other targets). - OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) - while read line; do - if [[ $line != "`realpath $PODS_ROOT`*" ]]; then - XCASSET_FILES+=("$line") - fi - done <<<"$OTHER_XCASSETS" - - printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -fi diff --git a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews.debug.xcconfig b/Pods/Target Support Files/Pods-TTNews/Pods-TTNews.debug.xcconfig deleted file mode 100644 index 7d5c743..0000000 --- a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews.debug.xcconfig +++ /dev/null @@ -1,5 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/AFNetworking" -isystem "${PODS_ROOT}/Headers/Public/DACircularProgress" -isystem "${PODS_ROOT}/Headers/Public/DKNightVersion" -isystem "${PODS_ROOT}/Headers/Public/FMDB" -isystem "${PODS_ROOT}/Headers/Public/MJExtension" -isystem "${PODS_ROOT}/Headers/Public/MJRefresh" -isystem "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" -isystem "${PODS_ROOT}/Headers/Public/SDWebImage" -isystem "${PODS_ROOT}/Headers/Public/SVProgressHUD" -isystem "${PODS_ROOT}/Headers/Public/pop" -OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking" -l"DACircularProgress" -l"DKNightVersion" -l"FMDB" -l"MJExtension" -l"MJRefresh" -l"NJKWebViewProgress" -l"SDWebImage" -l"SVProgressHUD" -l"c++" -l"pop" -l"sqlite3" -framework "CoreGraphics" -framework "ImageIO" -framework "MobileCoreServices" -framework "QuartzCore" -framework "Security" -framework "SystemConfiguration" -PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews.release.xcconfig b/Pods/Target Support Files/Pods-TTNews/Pods-TTNews.release.xcconfig deleted file mode 100644 index 7d5c743..0000000 --- a/Pods/Target Support Files/Pods-TTNews/Pods-TTNews.release.xcconfig +++ /dev/null @@ -1,5 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/AFNetworking" -isystem "${PODS_ROOT}/Headers/Public/DACircularProgress" -isystem "${PODS_ROOT}/Headers/Public/DKNightVersion" -isystem "${PODS_ROOT}/Headers/Public/FMDB" -isystem "${PODS_ROOT}/Headers/Public/MJExtension" -isystem "${PODS_ROOT}/Headers/Public/MJRefresh" -isystem "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" -isystem "${PODS_ROOT}/Headers/Public/SDWebImage" -isystem "${PODS_ROOT}/Headers/Public/SVProgressHUD" -isystem "${PODS_ROOT}/Headers/Public/pop" -OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking" -l"DACircularProgress" -l"DKNightVersion" -l"FMDB" -l"MJExtension" -l"MJRefresh" -l"NJKWebViewProgress" -l"SDWebImage" -l"SVProgressHUD" -l"c++" -l"pop" -l"sqlite3" -framework "CoreGraphics" -framework "ImageIO" -framework "MobileCoreServices" -framework "QuartzCore" -framework "Security" -framework "SystemConfiguration" -PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/Pods/Target Support Files/SDWebImage/SDWebImage-dummy.m b/Pods/Target Support Files/SDWebImage/SDWebImage-dummy.m deleted file mode 100644 index 86d2b5f..0000000 --- a/Pods/Target Support Files/SDWebImage/SDWebImage-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_SDWebImage : NSObject -@end -@implementation PodsDummy_SDWebImage -@end diff --git a/Pods/Target Support Files/SDWebImage/SDWebImage-prefix.pch b/Pods/Target Support Files/SDWebImage/SDWebImage-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Pods/Target Support Files/SDWebImage/SDWebImage-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Pods/Target Support Files/SDWebImage/SDWebImage.xcconfig b/Pods/Target Support Files/SDWebImage/SDWebImage.xcconfig deleted file mode 100644 index 862d727..0000000 --- a/Pods/Target Support Files/SDWebImage/SDWebImage.xcconfig +++ /dev/null @@ -1,5 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/SDWebImage" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -OTHER_LDFLAGS = -framework "ImageIO" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/SVProgressHUD/SVProgressHUD-dummy.m b/Pods/Target Support Files/SVProgressHUD/SVProgressHUD-dummy.m deleted file mode 100644 index 696032a..0000000 --- a/Pods/Target Support Files/SVProgressHUD/SVProgressHUD-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_SVProgressHUD : NSObject -@end -@implementation PodsDummy_SVProgressHUD -@end diff --git a/Pods/Target Support Files/SVProgressHUD/SVProgressHUD-prefix.pch b/Pods/Target Support Files/SVProgressHUD/SVProgressHUD-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Pods/Target Support Files/SVProgressHUD/SVProgressHUD-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Pods/Target Support Files/SVProgressHUD/SVProgressHUD.xcconfig b/Pods/Target Support Files/SVProgressHUD/SVProgressHUD.xcconfig deleted file mode 100644 index c7e047f..0000000 --- a/Pods/Target Support Files/SVProgressHUD/SVProgressHUD.xcconfig +++ /dev/null @@ -1,5 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/SVProgressHUD" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -OTHER_LDFLAGS = -framework "QuartzCore" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/pop/pop-dummy.m b/Pods/Target Support Files/pop/pop-dummy.m deleted file mode 100644 index 19ed261..0000000 --- a/Pods/Target Support Files/pop/pop-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_pop : NSObject -@end -@implementation PodsDummy_pop -@end diff --git a/Pods/Target Support Files/pop/pop-prefix.pch b/Pods/Target Support Files/pop/pop-prefix.pch deleted file mode 100644 index aa992a4..0000000 --- a/Pods/Target Support Files/pop/pop-prefix.pch +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __OBJC__ -#import -#endif - diff --git a/Pods/Target Support Files/pop/pop.xcconfig b/Pods/Target Support Files/pop/pop.xcconfig deleted file mode 100644 index e4aa3b3..0000000 --- a/Pods/Target Support Files/pop/pop.xcconfig +++ /dev/null @@ -1,7 +0,0 @@ -CLANG_CXX_LANGUAGE_STANDARD = c++11 -CLANG_CXX_LIBRARY = libc++ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/pop" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/DACircularProgress" "${PODS_ROOT}/Headers/Public/DKNightVersion" "${PODS_ROOT}/Headers/Public/FMDB" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" "${PODS_ROOT}/Headers/Public/NJKWebViewProgress" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SVProgressHUD" "${PODS_ROOT}/Headers/Public/pop" -OTHER_LDFLAGS = -l"c++" -PODS_ROOT = ${SRCROOT} -SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/pop/LICENSE b/Pods/pop/LICENSE deleted file mode 100644 index 642126f..0000000 --- a/Pods/pop/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -BSD License - -For Pop software - -Copyright (c) 2014, Facebook, Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Pods/pop/README.md b/Pods/pop/README.md deleted file mode 100644 index 9912a8a..0000000 --- a/Pods/pop/README.md +++ /dev/null @@ -1,203 +0,0 @@ -![pop](https://github.com/facebook/pop/blob/master/Images/pop.gif?raw=true) - -Pop is an extensible animation engine for iOS, tvOS, and OS X. In addition to basic static animations, it supports spring and decay dynamic animations, making it useful for building realistic, physics-based interactions. The API allows quick integration with existing Objective-C codebases and enables the animation of any property on any object. It's a mature and well-tested framework that drives all the animations and transitions in [Paper](http://www.facebook.com/paper). - -[![Build Status](https://travis-ci.org/facebook/pop.svg)](https://travis-ci.org/facebook/pop) - -## Installation - -Pop is available on [CocoaPods](http://cocoapods.org). Just add the following to your project Podfile: - -```ruby -pod 'pop', '~> 1.0' -``` - -Bugs are first fixed in master and then made available via a designated release. If you tend to live on the bleeding edge, you can use Pop from master with the following Podfile entry: - -```ruby -pod 'pop', :git => 'https://github.com/facebook/pop.git' -``` - -## Non-CocoaPods Installation - -### iOS 8 Embedded Framework -By adding the project to your project and adding pop.embedded framework to the Embedded Binaries section on the General tab of your app's target, you can set up pop in seconds! This also enables `@import pop` syntax with header modules. - -**Note**: because of some awkward limitations with Xcode, embedded binaries must share the same name as the module and must have `.framework` as an extension. This means that you'll see three pop.frameworks when adding embedded binaries (one for OS X, one for tvOS, and one for iOS). You'll need to be sure to add the iOS one, and since this list is populated in order of targets, it's safe to assume it's the second one. You can verify the correct one was chosen by checking the path next to the framework listed: `Debug-iphoneos`. The same principle applies on tvOS but instead of `Debug-iphoneos` look for `Debug-appletvos`. - -![Embedded Binaries](Images/EmbeddedBinaries.png?raw=true) - -**Note 2**: this method does not currently play nicely with workspaces. For some unknown reason, Xcode simply rejects adding pop.framework as an embedded binary when pop.xcodeproj is placed in the workspace. This only works when pop.xcodeproj is added as a subproject to the current target's project. - -### Advanced -Alternatively, you can add the project to your workspace and adopt the provided configuration files or manually copy the files under the pop subdirectory into your project. If installing manually, ensure the C++ standard library is also linked by including `-lc++` to your project linker flags. - -## Usage - -Pop adopts the Core Animation explicit animation programming model. Use by including the following import: - -```objective-c -# import -``` - -or if you're using the embedded framework: - -```objective-c -@import pop; -``` - -### Start, Stop & Update - -To start an animation, add it to the object you wish to animate: - -```objective-c -POPSpringAnimation *anim = [POPSpringAnimation animation]; -... -[layer pop_addAnimation:anim forKey:@"myKey"]; -``` - -To stop an animation, remove it from the object referencing the key specified on start: - -```objective-c -[layer pop_removeAnimationForKey:@"myKey"]; -``` - -The key can also be used to query for the existence of an animation. Updating the toValue of a running animation can provide the most seamless way to change course: - -```objective-c -anim = [layer pop_animationForKey:@"myKey"]; -if (anim) { - /* update to value to new destination */ - anim.toValue = @(42.0); -} else { - /* create and start a new animation */ - .... -} -``` - -While a layer was used in the above examples, the Pop interface is implemented as a category addition on NSObject. Any NSObject or subclass can be animated. - -### Types - -There are four concrete animation types: spring, decay, basic and custom. - -Spring animations can be used to give objects a delightful bounce. In this example, we use a spring animation to animate a layer's bounds from its current value to (0, 0, 400, 400): - -```objective-c -POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBounds]; -anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 400, 400)]; -[layer pop_addAnimation:anim forKey:@"size"]; -``` -Decay animations can be used to gradually slow an object to a halt. In this example, we decay a layer's positionX from it's current value and velocity 1000pts per second: - -```objective-c -POPDecayAnimation *anim = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPositionX]; -anim.velocity = @(1000.); -[layer pop_addAnimation:anim forKey:@"slide"]; -``` - -Basic animations can be used to interpolate values over a specified time period. To use an ease-in ease-out animation to animate a view's alpha from 0.0 to 1.0 over the default duration: -```objective-c -POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPViewAlpha]; -anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; -anim.fromValue = @(0.0); -anim.toValue = @(1.0); -[view pop_addAnimation:anim forKey:@"fade"]; -``` -`POPCustomAnimation` makes creating custom animations and transitions easier by handling CADisplayLink and associated time-step management. See header for more details. - - -### Properties - -The property animated is specified by the `POPAnimatableProperty` class. In this example we create a spring animation and explicitly set the animatable property corresponding to `-[CALayer bounds]`: - -```objective-c -POPSpringAnimation *anim = [POPSpringAnimation animation]; -anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBounds]; -``` - -The framework provides many common layer and view animatable properties out of box. You can animate a custom property by creating a new instance of the class. In this example, we declare a custom volume property: - -```objective-c -prop = [POPAnimatableProperty propertyWithName:@"com.foo.radio.volume" initializer:^(POPMutableAnimatableProperty *prop) { - // read value - prop.readBlock = ^(id obj, CGFloat values[]) { - values[0] = [obj volume]; - }; - // write value - prop.writeBlock = ^(id obj, const CGFloat values[]) { - [obj setVolume:values[0]]; - }; - // dynamics threshold - prop.threshold = 0.01; -}]; - -anim.property = prop; -``` - -For a complete listing of provided animatable properties, as well more information on declaring custom properties see `POPAnimatableProperty.h`. - - -### Debugging - -Here are a few tips when debugging. Pop obeys the Simulator's Toggle Slow Animations setting. Try enabling it to slow down animations and more easily observe interactions. - -Consider naming your animations. This will allow you to more easily identify them when referencing them, either via logging or in the debugger: - -```objective-c -anim.name = @"springOpen"; -``` - -Each animation comes with an associated tracer. The tracer allows you to record all animation-related events, in a fast and efficient manner, allowing you to query and analyze them after animation completion. The below example starts the tracer and configures it to log all events on animation completion: - -```objective-c -POPAnimationTracer *tracer = anim.tracer; -tracer.shouldLogAndResetOnCompletion = YES; -[tracer start]; -``` - -See `POPAnimationTracer.h` for more details. - -## Testing - -Pop has extensive unit test coverage. To install test dependencies, navigate to the root pop directory and type: - -```sh -pod install -``` - -Assuming CocoaPods is installed, this will include the necessary OCMock dependency to the unit test targets. - -## SceneKit - -Due to SceneKit requiring iOS 8 and OS X 10.9, POP's SceneKit extensions aren't provided out of box. Unfortunately, [weakly linked frameworks](https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html) cannot be used due to issues mentioned in the [Xcode 6.1 Release Notes](https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html). - -To remedy this, you can easily opt-in to use SceneKit! Simply add this to the Preprocessor Macros section of your Xcode Project: - -``` -POP_USE_SCENEKIT=1 -``` - -## Resources - -A collection of links to external resources that may prove valuable: - -* [AGGeometryKit+POP - Animating Quadrilaterals with Pop](https://github.com/hfossli/aggeometrykit-pop) -* [Apple – Core Animation Programming Guide](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/Introduction/Introduction.html) -* [iOS Development Tips – UIScrollView-like deceleration with Pop](http://iosdevtips.co/post/84571595353/replicating-uiscrollviews-deceleration-with-facebook) -* [Pop Playground – Repository of Pop animation examples](https://github.com/callmeed/pop-playground) -* [Pop Playground 2 – Playing with Facebook's framework](http://victorbaro.com/2014/05/pop-playground-playing-with-facebooks-framework/) -* [POP-MCAnimate – Concise syntax for the Pop animation framework](https://github.com/matthewcheok/POP-MCAnimate) -* [Popping - Great examples in one project](https://github.com/schneiderandre/popping) -* [Rebound – Spring Animations for Android](http://facebook.github.io/rebound/) -* [Tapity Tutorial – Getting Started with Pop](http://tapity.com/tutorial-getting-started-with-pop/) -* [Tweaks – Easily adjust parameters for iOS apps in development](https://github.com/facebook/tweaks) -* [POP Tutorial in 5 steps](https://github.com/maxmyers/FacebookPop) -* [VBFPopFlatButton – Flat animatable button, using Pop to transition between states](https://github.com/victorBaro/VBFPopFlatButton) - -## Contributing -See the CONTRIBUTING file for how to help out. - -## License - -Pop is released under a BSD License. See LICENSE file for details. diff --git a/Pods/pop/pop/POP.h b/Pods/pop/pop/POP.h deleted file mode 100644 index 72adba7..0000000 --- a/Pods/pop/pop/POP.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#ifndef POP_POP_H -#define POP_POP_H - -#import - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#endif /* POP_POP_H */ diff --git a/Pods/pop/pop/POPAction.h b/Pods/pop/pop/POPAction.h deleted file mode 100644 index 85cca19..0000000 --- a/Pods/pop/pop/POPAction.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#ifndef POPACTION_H -#define POPACTION_H - -#import - -#import - -#ifdef __cplusplus - -namespace POP { - - /** - @abstract Disables Core Animation actions using RAII. - @discussion The disablement of actions is scoped to the current transaction. - */ - class ActionDisabler - { - BOOL state; - - public: - ActionDisabler() POP_NOTHROW - { - state = [CATransaction disableActions]; - [CATransaction setDisableActions:YES]; - } - - ~ActionDisabler() - { - [CATransaction setDisableActions:state]; - } - }; - - /** - @abstract Enables Core Animation actions using RAII. - @discussion The enablement of actions is scoped to the current transaction. - */ - class ActionEnabler - { - BOOL state; - - public: - ActionEnabler() POP_NOTHROW - { - state = [CATransaction disableActions]; - [CATransaction setDisableActions:NO]; - } - - ~ActionEnabler() - { - [CATransaction setDisableActions:state]; - } - }; - -} - -#endif /* __cplusplus */ - -#endif /* POPACTION_H */ diff --git a/Pods/pop/pop/POPAnimatableProperty.h b/Pods/pop/pop/POPAnimatableProperty.h deleted file mode 100644 index 4b5cec9..0000000 --- a/Pods/pop/pop/POPAnimatableProperty.h +++ /dev/null @@ -1,251 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import - -#import - -@class POPMutableAnimatableProperty; - -/** - @abstract Describes an animatable property. - */ -@interface POPAnimatableProperty : NSObject - -/** - @abstract Property accessor. - @param name The name of the property. - @return The animatable property with that name or nil if it does not exist. - @discussion Common animatable properties are included by default. Use the provided constants to reference. - */ -+ (id)propertyWithName:(NSString *)name; - -/** - @abstract The designated initializer. - @param name The name of the property. - @param block The block used to configure the property on creation. - @return The animatable property with name if it exists, otherwise a newly created instance configured by block. - @discussion Custom properties should use reverse-DNS naming. A newly created instance is only mutable in the scope of block. Once constructed, a property becomes immutable. - */ -+ (id)propertyWithName:(NSString *)name initializer:(void (^)(POPMutableAnimatableProperty *prop))block; - -/** - @abstract The name of the property. - @discussion Used to uniquely identify an animatable property. - */ -@property (readonly, nonatomic, copy) NSString *name; - -/** - @abstract Block used to read values from a property into an array of floats. - */ -@property (readonly, nonatomic, copy) void (^readBlock)(id obj, CGFloat values[]); - -/** - @abstract Block used to write values from an array of floats into a property. - */ -@property (readonly, nonatomic, copy) void (^writeBlock)(id obj, const CGFloat values[]); - -/** - @abstract The threshold value used when determining completion of dynamics simulations. - */ -@property (readonly, nonatomic, assign) CGFloat threshold; - -@end - -/** - @abstract A mutable animatable property intended for configuration. - */ -@interface POPMutableAnimatableProperty : POPAnimatableProperty - -/** - @abstract A read-write version of POPAnimatableProperty name property. - */ -@property (readwrite, nonatomic, copy) NSString *name; - -/** - @abstract A read-write version of POPAnimatableProperty readBlock property. - */ -@property (readwrite, nonatomic, copy) void (^readBlock)(id obj, CGFloat values[]); - -/** - @abstract A read-write version of POPAnimatableProperty writeBlock property. - */ -@property (readwrite, nonatomic, copy) void (^writeBlock)(id obj, const CGFloat values[]); - -/** - @abstract A read-write version of POPAnimatableProperty threshold property. - */ -@property (readwrite, nonatomic, assign) CGFloat threshold; - -@end - -/** - Common CALayer property names. - */ -extern NSString * const kPOPLayerBackgroundColor; -extern NSString * const kPOPLayerBounds; -extern NSString * const kPOPLayerCornerRadius; -extern NSString * const kPOPLayerBorderWidth; -extern NSString * const kPOPLayerBorderColor; -extern NSString * const kPOPLayerOpacity; -extern NSString * const kPOPLayerPosition; -extern NSString * const kPOPLayerPositionX; -extern NSString * const kPOPLayerPositionY; -extern NSString * const kPOPLayerRotation; -extern NSString * const kPOPLayerRotationX; -extern NSString * const kPOPLayerRotationY; -extern NSString * const kPOPLayerScaleX; -extern NSString * const kPOPLayerScaleXY; -extern NSString * const kPOPLayerScaleY; -extern NSString * const kPOPLayerSize; -extern NSString * const kPOPLayerSubscaleXY; -extern NSString * const kPOPLayerSubtranslationX; -extern NSString * const kPOPLayerSubtranslationXY; -extern NSString * const kPOPLayerSubtranslationY; -extern NSString * const kPOPLayerSubtranslationZ; -extern NSString * const kPOPLayerTranslationX; -extern NSString * const kPOPLayerTranslationXY; -extern NSString * const kPOPLayerTranslationY; -extern NSString * const kPOPLayerTranslationZ; -extern NSString * const kPOPLayerZPosition; -extern NSString * const kPOPLayerShadowColor; -extern NSString * const kPOPLayerShadowOffset; -extern NSString * const kPOPLayerShadowOpacity; -extern NSString * const kPOPLayerShadowRadius; - -/** - Common CAShapeLayer property names. - */ -extern NSString * const kPOPShapeLayerStrokeStart; -extern NSString * const kPOPShapeLayerStrokeEnd; -extern NSString * const kPOPShapeLayerStrokeColor; -extern NSString * const kPOPShapeLayerFillColor; -extern NSString * const kPOPShapeLayerLineWidth; -extern NSString * const kPOPShapeLayerLineDashPhase; - -/** - Common NSLayoutConstraint property names. - */ -extern NSString * const kPOPLayoutConstraintConstant; - - -#if TARGET_OS_IPHONE - -/** - Common UIView property names. - */ -extern NSString * const kPOPViewAlpha; -extern NSString * const kPOPViewBackgroundColor; -extern NSString * const kPOPViewBounds; -extern NSString * const kPOPViewCenter; -extern NSString * const kPOPViewFrame; -extern NSString * const kPOPViewScaleX; -extern NSString * const kPOPViewScaleXY; -extern NSString * const kPOPViewScaleY; -extern NSString * const kPOPViewSize; -extern NSString * const kPOPViewTintColor; - -/** - Common UIScrollView property names. - */ -extern NSString * const kPOPScrollViewContentOffset; -extern NSString * const kPOPScrollViewContentSize; -extern NSString * const kPOPScrollViewZoomScale; -extern NSString * const kPOPScrollViewContentInset; -extern NSString * const kPOPScrollViewScrollIndicatorInsets; - -/** - Common UITableView property names. - */ -extern NSString * const kPOPTableViewContentOffset; -extern NSString * const kPOPTableViewContentSize; - -/** - Common UICollectionView property names. - */ -extern NSString * const kPOPCollectionViewContentOffset; -extern NSString * const kPOPCollectionViewContentSize; - -/** - Common UINavigationBar property names. - */ -extern NSString * const kPOPNavigationBarBarTintColor; - -/** - Common UIToolbar property names. - */ -extern NSString * const kPOPToolbarBarTintColor; - -/** - Common UITabBar property names. - */ -extern NSString * const kPOPTabBarBarTintColor; - -/** - Common UILabel property names. - */ -extern NSString * const kPOPLabelTextColor; - -#else - -/** - Common NSView property names. - */ -extern NSString * const kPOPViewFrame; -extern NSString * const kPOPViewBounds; -extern NSString * const kPOPViewAlphaValue; -extern NSString * const kPOPViewFrameRotation; -extern NSString * const kPOPViewFrameCenterRotation; -extern NSString * const kPOPViewBoundsRotation; - -/** - Common NSWindow property names. - */ -extern NSString * const kPOPWindowFrame; -extern NSString * const kPOPWindowAlphaValue; -extern NSString * const kPOPWindowBackgroundColor; - -#endif - -#if SCENEKIT_SDK_AVAILABLE - -/** - Common SceneKit property names. - */ -extern NSString * const kPOPSCNNodePosition; -extern NSString * const kPOPSCNNodePositionX; -extern NSString * const kPOPSCNNodePositionY; -extern NSString * const kPOPSCNNodePositionZ; -extern NSString * const kPOPSCNNodeTranslation; -extern NSString * const kPOPSCNNodeTranslationX; -extern NSString * const kPOPSCNNodeTranslationY; -extern NSString * const kPOPSCNNodeTranslationZ; -extern NSString * const kPOPSCNNodeRotation; -extern NSString * const kPOPSCNNodeRotationX; -extern NSString * const kPOPSCNNodeRotationY; -extern NSString * const kPOPSCNNodeRotationZ; -extern NSString * const kPOPSCNNodeRotationW; -extern NSString * const kPOPSCNNodeEulerAngles; -extern NSString * const kPOPSCNNodeEulerAnglesX; -extern NSString * const kPOPSCNNodeEulerAnglesY; -extern NSString * const kPOPSCNNodeEulerAnglesZ; -extern NSString * const kPOPSCNNodeOrientation; -extern NSString * const kPOPSCNNodeOrientationX; -extern NSString * const kPOPSCNNodeOrientationY; -extern NSString * const kPOPSCNNodeOrientationZ; -extern NSString * const kPOPSCNNodeOrientationW; -extern NSString * const kPOPSCNNodeScale; -extern NSString * const kPOPSCNNodeScaleX; -extern NSString * const kPOPSCNNodeScaleY; -extern NSString * const kPOPSCNNodeScaleZ; -extern NSString * const kPOPSCNNodeScaleXY; - -#endif diff --git a/Pods/pop/pop/POPAnimatableProperty.mm b/Pods/pop/pop/POPAnimatableProperty.mm deleted file mode 100644 index 91fd90a..0000000 --- a/Pods/pop/pop/POPAnimatableProperty.mm +++ /dev/null @@ -1,1307 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPAnimatableProperty.h" - -#import - -#import "POPAnimationRuntime.h" -#import "POPCGUtils.h" -#import "POPDefines.h" -#import "POPLayerExtras.h" - -// common threshold definitions -static CGFloat const kPOPThresholdColor = 0.01; -static CGFloat const kPOPThresholdPoint = 1.0; -static CGFloat const kPOPThresholdOpacity = 0.01; -static CGFloat const kPOPThresholdScale = 0.005; -static CGFloat const kPOPThresholdRotation = 0.01; -static CGFloat const kPOPThresholdRadius = 0.01; - -#pragma mark - Static - -// CALayer -NSString * const kPOPLayerBackgroundColor = @"backgroundColor"; -NSString * const kPOPLayerBounds = @"bounds"; -NSString * const kPOPLayerCornerRadius = @"cornerRadius"; -NSString * const kPOPLayerBorderWidth = @"borderWidth"; -NSString * const kPOPLayerBorderColor = @"borderColor"; -NSString * const kPOPLayerOpacity = @"opacity"; -NSString * const kPOPLayerPosition = @"position"; -NSString * const kPOPLayerPositionX = @"positionX"; -NSString * const kPOPLayerPositionY = @"positionY"; -NSString * const kPOPLayerRotation = @"rotation"; -NSString * const kPOPLayerRotationX = @"rotationX"; -NSString * const kPOPLayerRotationY = @"rotationY"; -NSString * const kPOPLayerScaleX = @"scaleX"; -NSString * const kPOPLayerScaleXY = @"scaleXY"; -NSString * const kPOPLayerScaleY = @"scaleY"; -NSString * const kPOPLayerSize = @"size"; -NSString * const kPOPLayerSubscaleXY = @"subscaleXY"; -NSString * const kPOPLayerSubtranslationX = @"subtranslationX"; -NSString * const kPOPLayerSubtranslationXY = @"subtranslationXY"; -NSString * const kPOPLayerSubtranslationY = @"subtranslationY"; -NSString * const kPOPLayerSubtranslationZ = @"subtranslationZ"; -NSString * const kPOPLayerTranslationX = @"translationX"; -NSString * const kPOPLayerTranslationXY = @"translationXY"; -NSString * const kPOPLayerTranslationY = @"translationY"; -NSString * const kPOPLayerTranslationZ = @"translationZ"; -NSString * const kPOPLayerZPosition = @"zPosition"; -NSString * const kPOPLayerShadowColor = @"shadowColor"; -NSString * const kPOPLayerShadowOffset = @"shadowOffset"; -NSString * const kPOPLayerShadowOpacity = @"shadowOpacity"; -NSString * const kPOPLayerShadowRadius = @"shadowRadius"; - -// CAShapeLayer -NSString * const kPOPShapeLayerStrokeStart = @"shapeLayer.strokeStart"; -NSString * const kPOPShapeLayerStrokeEnd = @"shapeLayer.strokeEnd"; -NSString * const kPOPShapeLayerStrokeColor = @"shapeLayer.strokeColor"; -NSString * const kPOPShapeLayerFillColor = @"shapeLayer.fillColor"; -NSString * const kPOPShapeLayerLineWidth = @"shapeLayer.lineWidth"; -NSString * const kPOPShapeLayerLineDashPhase = @"shapeLayer.lineDashPhase"; - -// NSLayoutConstraint -NSString * const kPOPLayoutConstraintConstant = @"layoutConstraint.constant"; - -#if TARGET_OS_IPHONE - -// UIView -NSString * const kPOPViewAlpha = @"view.alpha"; -NSString * const kPOPViewBackgroundColor = @"view.backgroundColor"; -NSString * const kPOPViewBounds = kPOPLayerBounds; -NSString * const kPOPViewCenter = @"view.center"; -NSString * const kPOPViewFrame = @"view.frame"; -NSString * const kPOPViewScaleX = @"view.scaleX"; -NSString * const kPOPViewScaleXY = @"view.scaleXY"; -NSString * const kPOPViewScaleY = @"view.scaleY"; -NSString * const kPOPViewSize = kPOPLayerSize; -NSString * const kPOPViewTintColor = @"view.tintColor"; - -// UIScrollView -NSString * const kPOPScrollViewContentOffset = @"scrollView.contentOffset"; -NSString * const kPOPScrollViewContentSize = @"scrollView.contentSize"; -NSString * const kPOPScrollViewZoomScale = @"scrollView.zoomScale"; -NSString * const kPOPScrollViewContentInset = @"scrollView.contentInset"; -NSString * const kPOPScrollViewScrollIndicatorInsets = @"scrollView.scrollIndicatorInsets"; - -// UITableView -NSString * const kPOPTableViewContentOffset = kPOPScrollViewContentOffset; -NSString * const kPOPTableViewContentSize = kPOPScrollViewContentSize; - -// UICollectionView -NSString * const kPOPCollectionViewContentOffset = kPOPScrollViewContentOffset; -NSString * const kPOPCollectionViewContentSize = kPOPScrollViewContentSize; - -// UINavigationBar -NSString * const kPOPNavigationBarBarTintColor = @"navigationBar.barTintColor"; - -// UIToolbar -NSString * const kPOPToolbarBarTintColor = kPOPNavigationBarBarTintColor; - -// UITabBar -NSString * const kPOPTabBarBarTintColor = kPOPNavigationBarBarTintColor; - -// UILabel -NSString * const kPOPLabelTextColor = @"label.textColor"; - -#else - -// NSView -NSString * const kPOPViewFrame = @"view.frame"; -NSString * const kPOPViewBounds = @"view.bounds"; -NSString * const kPOPViewAlphaValue = @"view.alphaValue"; -NSString * const kPOPViewFrameRotation = @"view.frameRotation"; -NSString * const kPOPViewFrameCenterRotation = @"view.frameCenterRotation"; -NSString * const kPOPViewBoundsRotation = @"view.boundsRotation"; - -// NSWindow -NSString * const kPOPWindowFrame = @"window.frame"; -NSString * const kPOPWindowAlphaValue = @"window.alphaValue"; -NSString * const kPOPWindowBackgroundColor = @"window.backgroundColor"; - -#endif - -#if SCENEKIT_SDK_AVAILABLE - -// SceneKit -NSString * const kPOPSCNNodePosition = @"scnode.position"; -NSString * const kPOPSCNNodePositionX = @"scnnode.position.x"; -NSString * const kPOPSCNNodePositionY = @"scnnode.position.y"; -NSString * const kPOPSCNNodePositionZ = @"scnnode.position.z"; -NSString * const kPOPSCNNodeTranslation = @"scnnode.translation"; -NSString * const kPOPSCNNodeTranslationX = @"scnnode.translation.x"; -NSString * const kPOPSCNNodeTranslationY = @"scnnode.translation.y"; -NSString * const kPOPSCNNodeTranslationZ = @"scnnode.translation.z"; -NSString * const kPOPSCNNodeRotation = @"scnnode.rotation"; -NSString * const kPOPSCNNodeRotationX = @"scnnode.rotation.x"; -NSString * const kPOPSCNNodeRotationY = @"scnnode.rotation.y"; -NSString * const kPOPSCNNodeRotationZ = @"scnnode.rotation.z"; -NSString * const kPOPSCNNodeRotationW = @"scnnode.rotation.w"; -NSString * const kPOPSCNNodeEulerAngles = @"scnnode.eulerAngles"; -NSString * const kPOPSCNNodeEulerAnglesX = @"scnnode.eulerAngles.x"; -NSString * const kPOPSCNNodeEulerAnglesY = @"scnnode.eulerAngles.y"; -NSString * const kPOPSCNNodeEulerAnglesZ = @"scnnode.eulerAngles.z"; -NSString * const kPOPSCNNodeOrientation = @"scnnode.orientation"; -NSString * const kPOPSCNNodeOrientationX = @"scnnode.orientation.x"; -NSString * const kPOPSCNNodeOrientationY = @"scnnode.orientation.y"; -NSString * const kPOPSCNNodeOrientationZ = @"scnnode.orientation.z"; -NSString * const kPOPSCNNodeOrientationW = @"scnnode.orientation.w"; -NSString * const kPOPSCNNodeScale = @"scnnode.scale"; -NSString * const kPOPSCNNodeScaleX = @"scnnode.scale.x"; -NSString * const kPOPSCNNodeScaleY = @"scnnode.scale.y"; -NSString * const kPOPSCNNodeScaleZ = @"scnnode.scale.z"; -NSString * const kPOPSCNNodeScaleXY = @"scnnode.scale.xy"; - -#endif - -/** - State structure internal to static animatable property. - */ -typedef struct -{ - NSString *name; - pop_animatable_read_block readBlock; - pop_animatable_write_block writeBlock; - CGFloat threshold; -} _POPStaticAnimatablePropertyState; -typedef _POPStaticAnimatablePropertyState POPStaticAnimatablePropertyState; - -static POPStaticAnimatablePropertyState _staticStates[] = -{ - /* CALayer */ - - {kPOPLayerBackgroundColor, - ^(CALayer *obj, CGFloat values[]) { - POPCGColorGetRGBAComponents(obj.backgroundColor, values); - }, - ^(CALayer *obj, const CGFloat values[]) { - CGColorRef color = POPCGColorRGBACreate(values); - [obj setBackgroundColor:color]; - CGColorRelease(color); - }, - kPOPThresholdColor - }, - - {kPOPLayerBounds, - ^(CALayer *obj, CGFloat values[]) { - values_from_rect(values, [obj bounds]); - }, - ^(CALayer *obj, const CGFloat values[]) { - [obj setBounds:values_to_rect(values)]; - }, - kPOPThresholdPoint - }, - - {kPOPLayerCornerRadius, - ^(CALayer *obj, CGFloat values[]) { - values[0] = [obj cornerRadius]; - }, - ^(CALayer *obj, const CGFloat values[]) { - [obj setCornerRadius:values[0]]; - }, - kPOPThresholdRadius - }, - - {kPOPLayerBorderWidth, - ^(CALayer *obj, CGFloat values[]) { - values[0] = [obj borderWidth]; - }, - ^(CALayer *obj, const CGFloat values[]) { - [obj setBorderWidth:values[0]]; - }, - 0.01 - }, - - {kPOPLayerBorderColor, - ^(CALayer *obj, CGFloat values[]) { - POPCGColorGetRGBAComponents(obj.borderColor, values); - }, - ^(CALayer *obj, const CGFloat values[]) { - CGColorRef color = POPCGColorRGBACreate(values); - [obj setBorderColor:color]; - CGColorRelease(color); - }, - kPOPThresholdColor - }, - - {kPOPLayerPosition, - ^(CALayer *obj, CGFloat values[]) { - values_from_point(values, [(CALayer *)obj position]); - }, - ^(CALayer *obj, const CGFloat values[]) { - [obj setPosition:values_to_point(values)]; - }, - kPOPThresholdPoint - }, - - {kPOPLayerPositionX, - ^(CALayer *obj, CGFloat values[]) { - values[0] = [(CALayer *)obj position].x; - }, - ^(CALayer *obj, const CGFloat values[]) { - CGPoint p = [(CALayer *)obj position]; - p.x = values[0]; - [obj setPosition:p]; - }, - kPOPThresholdPoint - }, - - {kPOPLayerPositionY, - ^(CALayer *obj, CGFloat values[]) { - values[0] = [(CALayer *)obj position].y; - }, - ^(CALayer *obj, const CGFloat values[]) { - CGPoint p = [(CALayer *)obj position]; - p.y = values[0]; - [obj setPosition:p]; - }, - kPOPThresholdPoint - }, - - {kPOPLayerOpacity, - ^(CALayer *obj, CGFloat values[]) { - values[0] = [obj opacity]; - }, - ^(CALayer *obj, const CGFloat values[]) { - [obj setOpacity:((float)values[0])]; - }, - kPOPThresholdOpacity - }, - - {kPOPLayerScaleX, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetScaleX(obj); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetScaleX(obj, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPLayerScaleY, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetScaleY(obj); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetScaleY(obj, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPLayerScaleXY, - ^(CALayer *obj, CGFloat values[]) { - values_from_point(values, POPLayerGetScaleXY(obj)); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetScaleXY(obj, values_to_point(values)); - }, - kPOPThresholdScale - }, - - {kPOPLayerSubscaleXY, - ^(CALayer *obj, CGFloat values[]) { - values_from_point(values, POPLayerGetSubScaleXY(obj)); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetSubScaleXY(obj, values_to_point(values)); - }, - kPOPThresholdScale - }, - - {kPOPLayerTranslationX, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetTranslationX(obj); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetTranslationX(obj, values[0]); - }, - kPOPThresholdPoint - }, - - {kPOPLayerTranslationY, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetTranslationY(obj); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetTranslationY(obj, values[0]); - }, - kPOPThresholdPoint - }, - - {kPOPLayerTranslationZ, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetTranslationZ(obj); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetTranslationZ(obj, values[0]); - }, - kPOPThresholdPoint - }, - - {kPOPLayerTranslationXY, - ^(CALayer *obj, CGFloat values[]) { - values_from_point(values, POPLayerGetTranslationXY(obj)); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetTranslationXY(obj, values_to_point(values)); - }, - kPOPThresholdPoint - }, - - {kPOPLayerSubtranslationX, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetSubTranslationX(obj); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetSubTranslationX(obj, values[0]); - }, - kPOPThresholdPoint - }, - - {kPOPLayerSubtranslationY, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetSubTranslationY(obj); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetSubTranslationY(obj, values[0]); - }, - kPOPThresholdPoint - }, - - {kPOPLayerSubtranslationZ, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetSubTranslationZ(obj); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetSubTranslationZ(obj, values[0]); - }, - kPOPThresholdPoint - }, - - {kPOPLayerSubtranslationXY, - ^(CALayer *obj, CGFloat values[]) { - values_from_point(values, POPLayerGetSubTranslationXY(obj)); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetSubTranslationXY(obj, values_to_point(values)); - }, - kPOPThresholdPoint - }, - - {kPOPLayerZPosition, - ^(CALayer *obj, CGFloat values[]) { - values[0] = [obj zPosition]; - }, - ^(CALayer *obj, const CGFloat values[]) { - [obj setZPosition:values[0]]; - }, - kPOPThresholdPoint - }, - - {kPOPLayerSize, - ^(CALayer *obj, CGFloat values[]) { - values_from_size(values, [obj bounds].size); - }, - ^(CALayer *obj, const CGFloat values[]) { - CGSize size = values_to_size(values); - if (size.width < 0. || size.height < 0.) - return; - - CGRect b = [obj bounds]; - b.size = size; - [obj setBounds:b]; - }, - kPOPThresholdPoint - }, - - {kPOPLayerRotation, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetRotation(obj); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetRotation(obj, values[0]); - }, - kPOPThresholdRotation - }, - - {kPOPLayerRotationY, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetRotationY(obj); - }, - ^(id obj, const CGFloat values[]) { - POPLayerSetRotationY(obj, values[0]); - }, - kPOPThresholdRotation - }, - - {kPOPLayerRotationX, - ^(CALayer *obj, CGFloat values[]) { - values[0] = POPLayerGetRotationX(obj); - }, - ^(CALayer *obj, const CGFloat values[]) { - POPLayerSetRotationX(obj, values[0]); - }, - kPOPThresholdRotation - }, - - {kPOPLayerShadowColor, - ^(CALayer *obj, CGFloat values[]) { - POPCGColorGetRGBAComponents(obj.shadowColor, values); - }, - ^(CALayer *obj, const CGFloat values[]) { - CGColorRef color = POPCGColorRGBACreate(values); - [obj setShadowColor:color]; - CGColorRelease(color); - }, - 0.01 - }, - - {kPOPLayerShadowOffset, - ^(CALayer *obj, CGFloat values[]) { - values_from_size(values, [obj shadowOffset]); - }, - ^(CALayer *obj, const CGFloat values[]) { - CGSize size = values_to_size(values); - [obj setShadowOffset:size]; - }, - 0.01 - }, - - {kPOPLayerShadowOpacity, - ^(CALayer *obj, CGFloat values[]) { - values[0] = [obj shadowOpacity]; - }, - ^(CALayer *obj, const CGFloat values[]) { - [obj setShadowOpacity:values[0]]; - }, - kPOPThresholdOpacity - }, - - {kPOPLayerShadowRadius, - ^(CALayer *obj, CGFloat values[]) { - values[0] = [obj shadowRadius]; - }, - ^(CALayer *obj, const CGFloat values[]) { - [obj setShadowRadius:values[0]]; - }, - kPOPThresholdRadius - }, - - /* CAShapeLayer */ - - {kPOPShapeLayerStrokeStart, - ^(CAShapeLayer *obj, CGFloat values[]) { - values[0] = obj.strokeStart; - }, - ^(CAShapeLayer *obj, const CGFloat values[]) { - obj.strokeStart = values[0]; - }, - 0.01 - }, - - {kPOPShapeLayerStrokeEnd, - ^(CAShapeLayer *obj, CGFloat values[]) { - values[0] = obj.strokeEnd; - }, - ^(CAShapeLayer *obj, const CGFloat values[]) { - obj.strokeEnd = values[0]; - }, - 0.01 - }, - - {kPOPShapeLayerStrokeColor, - ^(CAShapeLayer *obj, CGFloat values[]) { - POPCGColorGetRGBAComponents(obj.strokeColor, values); - }, - ^(CAShapeLayer *obj, const CGFloat values[]) { - CGColorRef color = POPCGColorRGBACreate(values); - [obj setStrokeColor:color]; - CGColorRelease(color); - }, - kPOPThresholdColor - }, - - {kPOPShapeLayerFillColor, - ^(CAShapeLayer *obj, CGFloat values[]) { - POPCGColorGetRGBAComponents(obj.fillColor, values); - }, - ^(CAShapeLayer *obj, const CGFloat values[]) { - CGColorRef color = POPCGColorRGBACreate(values); - [obj setFillColor:color]; - CGColorRelease(color); - }, - kPOPThresholdColor - }, - - {kPOPShapeLayerLineWidth, - ^(CAShapeLayer *obj, CGFloat values[]) { - values[0] = obj.lineWidth; - }, - ^(CAShapeLayer *obj, const CGFloat values[]) { - obj.lineWidth = values[0]; - }, - 0.01 - }, - - {kPOPShapeLayerLineDashPhase, - ^(CAShapeLayer *obj, CGFloat values[]) { - values[0] = obj.lineDashPhase; - }, - ^(CAShapeLayer *obj, const CGFloat values[]) { - obj.lineDashPhase = values[0]; - }, - 0.01 - }, - - {kPOPLayoutConstraintConstant, - ^(NSLayoutConstraint *obj, CGFloat values[]) { - values[0] = obj.constant; - }, - ^(NSLayoutConstraint *obj, const CGFloat values[]) { - obj.constant = values[0]; - }, - 0.01 - }, - -#if TARGET_OS_IPHONE - - /* UIView */ - - {kPOPViewAlpha, - ^(UIView *obj, CGFloat values[]) { - values[0] = obj.alpha; - }, - ^(UIView *obj, const CGFloat values[]) { - obj.alpha = values[0]; - }, - kPOPThresholdOpacity - }, - - {kPOPViewBackgroundColor, - ^(UIView *obj, CGFloat values[]) { - POPUIColorGetRGBAComponents(obj.backgroundColor, values); - }, - ^(UIView *obj, const CGFloat values[]) { - obj.backgroundColor = POPUIColorRGBACreate(values); - }, - kPOPThresholdColor - }, - - {kPOPViewCenter, - ^(UIView *obj, CGFloat values[]) { - values_from_point(values, obj.center); - }, - ^(UIView *obj, const CGFloat values[]) { - obj.center = values_to_point(values); - }, - kPOPThresholdPoint - }, - - {kPOPViewFrame, - ^(UIView *obj, CGFloat values[]) { - values_from_rect(values, obj.frame); - }, - ^(UIView *obj, const CGFloat values[]) { - obj.frame = values_to_rect(values); - }, - kPOPThresholdPoint - }, - - {kPOPViewScaleX, - ^(UIView *obj, CGFloat values[]) { - values[0] = POPLayerGetScaleX(obj.layer); - }, - ^(UIView *obj, const CGFloat values[]) { - POPLayerSetScaleX(obj.layer, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPViewScaleY, - ^(UIView *obj, CGFloat values[]) { - values[0] = POPLayerGetScaleY(obj.layer); - }, - ^(UIView *obj, const CGFloat values[]) { - POPLayerSetScaleY(obj.layer, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPViewScaleXY, - ^(UIView *obj, CGFloat values[]) { - values_from_point(values, POPLayerGetScaleXY(obj.layer)); - }, - ^(UIView *obj, const CGFloat values[]) { - POPLayerSetScaleXY(obj.layer, values_to_point(values)); - }, - kPOPThresholdScale - }, - - {kPOPViewTintColor, - ^(UIView *obj, CGFloat values[]) { - POPUIColorGetRGBAComponents(obj.tintColor, values); - }, - ^(UIView *obj, const CGFloat values[]) { - obj.tintColor = POPUIColorRGBACreate(values); - }, - kPOPThresholdColor - }, - - /* UIScrollView */ - - {kPOPScrollViewContentOffset, - ^(UIScrollView *obj, CGFloat values[]) { - values_from_point(values, obj.contentOffset); - }, - ^(UIScrollView *obj, const CGFloat values[]) { - [obj setContentOffset:values_to_point(values) animated:NO]; - }, - kPOPThresholdPoint - }, - - {kPOPScrollViewContentSize, - ^(UIScrollView *obj, CGFloat values[]) { - values_from_size(values, obj.contentSize); - }, - ^(UIScrollView *obj, const CGFloat values[]) { - obj.contentSize = values_to_size(values); - }, - kPOPThresholdPoint - }, - - {kPOPScrollViewZoomScale, - ^(UIScrollView *obj, CGFloat values[]) { - values[0]=obj.zoomScale; - }, - ^(UIScrollView *obj, const CGFloat values[]) { - obj.zoomScale=values[0]; - }, - kPOPThresholdScale - }, - - {kPOPScrollViewContentInset, - ^(UIScrollView *obj, CGFloat values[]) { - values[0] = obj.contentInset.top; - values[1] = obj.contentInset.left; - values[2] = obj.contentInset.bottom; - values[3] = obj.contentInset.right; - }, - ^(UIScrollView *obj, const CGFloat values[]) { - obj.contentInset = values_to_edge_insets(values); - }, - kPOPThresholdPoint - }, - - {kPOPScrollViewScrollIndicatorInsets, - ^(UIScrollView *obj, CGFloat values[]) { - values[0] = obj.scrollIndicatorInsets.top; - values[1] = obj.scrollIndicatorInsets.left; - values[2] = obj.scrollIndicatorInsets.bottom; - values[3] = obj.scrollIndicatorInsets.right; - }, - ^(UIScrollView *obj, const CGFloat values[]) { - obj.scrollIndicatorInsets = values_to_edge_insets(values); - }, - kPOPThresholdPoint - }, - - /* UINavigationBar */ - - {kPOPNavigationBarBarTintColor, - ^(UINavigationBar *obj, CGFloat values[]) { - POPUIColorGetRGBAComponents(obj.barTintColor, values); - }, - ^(UINavigationBar *obj, const CGFloat values[]) { - obj.barTintColor = POPUIColorRGBACreate(values); - }, - kPOPThresholdColor - }, - - /* UILabel */ - - {kPOPLabelTextColor, - ^(UILabel *obj, CGFloat values[]) { - POPUIColorGetRGBAComponents(obj.textColor, values); - }, - ^(UILabel *obj, const CGFloat values[]) { - obj.textColor = POPUIColorRGBACreate(values); - }, - kPOPThresholdColor - }, - -#else - - /* NSView */ - - {kPOPViewFrame, - ^(NSView *obj, CGFloat values[]) { - values_from_rect(values, NSRectToCGRect(obj.frame)); - }, - ^(NSView *obj, const CGFloat values[]) { - obj.frame = NSRectFromCGRect(values_to_rect(values)); - }, - kPOPThresholdPoint - }, - - {kPOPViewBounds, - ^(NSView *obj, CGFloat values[]) { - values_from_rect(values, NSRectToCGRect(obj.frame)); - }, - ^(NSView *obj, const CGFloat values[]) { - obj.bounds = NSRectFromCGRect(values_to_rect(values)); - }, - kPOPThresholdPoint - }, - - {kPOPViewAlphaValue, - ^(NSView *obj, CGFloat values[]) { - values[0] = obj.alphaValue; - }, - ^(NSView *obj, const CGFloat values[]) { - obj.alphaValue = values[0]; - }, - kPOPThresholdOpacity - }, - - {kPOPViewFrameRotation, - ^(NSView *obj, CGFloat values[]) { - values[0] = obj.frameRotation; - }, - ^(NSView *obj, const CGFloat values[]) { - obj.frameRotation = values[0]; - }, - kPOPThresholdRotation - }, - - {kPOPViewFrameCenterRotation, - ^(NSView *obj, CGFloat values[]) { - values[0] = obj.frameCenterRotation; - }, - ^(NSView *obj, const CGFloat values[]) { - obj.frameCenterRotation = values[0]; - }, - kPOPThresholdRotation - }, - - {kPOPViewBoundsRotation, - ^(NSView *obj, CGFloat values[]) { - values[0] = obj.boundsRotation; - }, - ^(NSView *obj, const CGFloat values[]) { - obj.boundsRotation = values[0]; - }, - kPOPThresholdRotation - }, - - /* NSWindow */ - - {kPOPWindowFrame, - ^(NSWindow *obj, CGFloat values[]) { - values_from_rect(values, NSRectToCGRect(obj.frame)); - }, - ^(NSWindow *obj, const CGFloat values[]) { - [obj setFrame:NSRectFromCGRect(values_to_rect(values)) display:YES]; - }, - kPOPThresholdPoint - }, - - {kPOPWindowAlphaValue, - ^(NSWindow *obj, CGFloat values[]) { - values[0] = obj.alphaValue; - }, - ^(NSWindow *obj, const CGFloat values[]) { - obj.alphaValue = values[0]; - }, - kPOPThresholdOpacity - }, - - {kPOPWindowBackgroundColor, - ^(NSWindow *obj, CGFloat values[]) { - POPNSColorGetRGBAComponents(obj.backgroundColor, values); - }, - ^(NSWindow *obj, const CGFloat values[]) { - obj.backgroundColor = POPNSColorRGBACreate(values); - }, - kPOPThresholdColor - }, - -#endif - -#if SCENEKIT_SDK_AVAILABLE - - /* SceneKit */ - - {kPOPSCNNodePosition, - ^(SCNNode *obj, CGFloat values[]) { - values_from_vec3(values, obj.position); - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.position = values_to_vec3(values); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodePositionX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.position.x; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.position = SCNVector3Make(values[0], obj.position.y, obj.position.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodePositionY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.position.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.position = SCNVector3Make(obj.position.x, values[0], obj.position.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodePositionZ, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.position.z; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.position = SCNVector3Make(obj.position.x, obj.position.y, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeTranslation, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.transform.m41; - values[1] = obj.transform.m42; - values[2] = obj.transform.m43; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.transform = SCNMatrix4MakeTranslation(values[0], values[1], values[2]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeTranslationX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.transform.m41; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.transform = SCNMatrix4MakeTranslation(values[0], obj.transform.m42, obj.transform.m43); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeTranslationY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.transform.m42; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.transform = SCNMatrix4MakeTranslation(obj.transform.m41, values[0], obj.transform.m43); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeTranslationY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.transform.m43; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.transform = SCNMatrix4MakeTranslation(obj.transform.m41, obj.transform.m42, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeRotation, - ^(SCNNode *obj, CGFloat values[]) { - values_from_vec4(values, obj.rotation); - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.rotation = values_to_vec4(values); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeRotationX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.rotation.x; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.rotation = SCNVector4Make(1.0, obj.rotation.y, obj.rotation.z, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeRotationY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.rotation.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.rotation = SCNVector4Make(obj.rotation.x, 1.0, obj.rotation.z, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeRotationZ, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.rotation.z; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.rotation = SCNVector4Make(obj.rotation.x, obj.rotation.y, 1.0, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeRotationW, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.rotation.w; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.rotation = SCNVector4Make(obj.rotation.x, obj.rotation.y, obj.rotation.z, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeEulerAngles, - ^(SCNNode *obj, CGFloat values[]) { - values_from_vec3(values, obj.eulerAngles); - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.eulerAngles = values_to_vec3(values); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeEulerAnglesX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.eulerAngles.x; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.eulerAngles = SCNVector3Make(values[0], obj.eulerAngles.y, obj.eulerAngles.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeEulerAnglesY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.eulerAngles.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.eulerAngles = SCNVector3Make(obj.eulerAngles.x, values[0], obj.eulerAngles.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeEulerAnglesZ, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.eulerAngles.z; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.eulerAngles = SCNVector3Make(obj.eulerAngles.x, obj.eulerAngles.y, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeOrientation, - ^(SCNNode *obj, CGFloat values[]) { - values_from_vec4(values, obj.orientation); - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.orientation = values_to_vec4(values); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeOrientationX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.orientation.x; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.orientation = SCNVector4Make(values[0], obj.orientation.y, obj.orientation.z, obj.orientation.w); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeOrientationY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.orientation.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.orientation = SCNVector4Make(obj.orientation.x, values[0], obj.orientation.z, obj.orientation.w); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeOrientationZ, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.orientation.z; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.orientation = SCNVector4Make(obj.orientation.x, obj.orientation.y, values[0], obj.orientation.w); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeOrientationW, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.orientation.w; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.orientation = SCNVector4Make(obj.orientation.x, obj.orientation.y, obj.orientation.z, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeScale, - ^(SCNNode *obj, CGFloat values[]) { - values_from_vec3(values, obj.scale); - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.scale = values_to_vec3(values); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeScaleX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.scale.x; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.scale = SCNVector3Make(values[0], obj.scale.y, obj.scale.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeScaleY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.scale.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.position = SCNVector3Make(obj.scale.x, values[0], obj.scale.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeScaleZ, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.scale.z; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.scale = SCNVector3Make(obj.scale.x, obj.scale.y, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeScaleXY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.scale.x; - values[1] = obj.scale.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.scale = SCNVector3Make(values[0], values[1], obj.scale.z); - }, - kPOPThresholdScale - }, - -#endif - -}; - -static NSUInteger staticIndexWithName(NSString *aName) -{ - NSUInteger idx = 0; - - while (idx < POP_ARRAY_COUNT(_staticStates)) { - if ([_staticStates[idx].name isEqualToString:aName]) - return idx; - idx++; - } - - return NSNotFound; -} - -/** - Concrete static property class. - */ -@interface POPStaticAnimatableProperty : POPAnimatableProperty -{ -@public - POPStaticAnimatablePropertyState *_state; -} -@end - -@implementation POPStaticAnimatableProperty - -- (NSString *)name -{ - return _state->name; -} - -- (pop_animatable_read_block)readBlock -{ - return _state->readBlock; -} - -- (pop_animatable_write_block)writeBlock -{ - return _state->writeBlock; -} - -- (CGFloat)threshold -{ - return _state->threshold; -} - -@end - -#pragma mark - Concrete - -/** - Concrete immutable property class. - */ -@interface POPConcreteAnimatableProperty : POPAnimatableProperty -- (instancetype)initWithName:(NSString *)name readBlock:(pop_animatable_read_block)read writeBlock:(pop_animatable_write_block)write threshold:(CGFloat)threshold; -@end - -@implementation POPConcreteAnimatableProperty - -// default synthesis -@synthesize name, readBlock, writeBlock, threshold; - -- (instancetype)initWithName:(NSString *)aName readBlock:(pop_animatable_read_block)aReadBlock writeBlock:(pop_animatable_write_block)aWriteBlock threshold:(CGFloat)aThreshold -{ - self = [super init]; - if (nil != self) { - name = [aName copy]; - readBlock = [aReadBlock copy]; - writeBlock = [aWriteBlock copy]; - threshold = aThreshold; - } - return self; -} -@end - -#pragma mark - Mutable - -@implementation POPMutableAnimatableProperty - -// default synthesis -@synthesize name, readBlock, writeBlock, threshold; - -@end - -#pragma mark - Cluster - -/** - Singleton placeholder property class to support class cluster. - */ -@interface POPPlaceholderAnimatableProperty : POPAnimatableProperty - -@end - -@implementation POPPlaceholderAnimatableProperty - -// default synthesis -@synthesize name, readBlock, writeBlock, threshold; - -@end - -/** - Cluster class. - */ -@implementation POPAnimatableProperty - -// avoid creating backing ivars -@dynamic name, readBlock, writeBlock, threshold; - -static POPAnimatableProperty *placeholder = nil; - -+ (void)initialize -{ - if (self == [POPAnimatableProperty class]) { - placeholder = [POPPlaceholderAnimatableProperty alloc]; - } -} - -+ (id)allocWithZone:(struct _NSZone *)zone -{ - if (self == [POPAnimatableProperty class]) { - if (nil == placeholder) { - placeholder = [super allocWithZone:zone]; - } - return placeholder; - } - return [super allocWithZone:zone]; -} - -- (id)copyWithZone:(NSZone *)zone -{ - if ([self isKindOfClass:[POPMutableAnimatableProperty class]]) { - POPConcreteAnimatableProperty *copyProperty = [[POPConcreteAnimatableProperty alloc] initWithName:self.name readBlock:self.readBlock writeBlock:self.writeBlock threshold:self.threshold]; - return copyProperty; - } else { - return self; - } -} - -- (id)mutableCopyWithZone:(NSZone *)zone -{ - POPMutableAnimatableProperty *copyProperty = [[POPMutableAnimatableProperty alloc] init]; - copyProperty.name = self.name; - copyProperty.readBlock = self.readBlock; - copyProperty.writeBlock = self.writeBlock; - copyProperty.threshold = self.threshold; - return copyProperty; -} - -+ (id)propertyWithName:(NSString *)aName -{ - return [self propertyWithName:aName initializer:NULL]; -} - -+ (id)propertyWithName:(NSString *)aName initializer:(void (^)(POPMutableAnimatableProperty *prop))aBlock -{ - POPAnimatableProperty *prop = nil; - - static NSMutableDictionary *_propertyDict = nil; - if (nil == _propertyDict) { - _propertyDict = [[NSMutableDictionary alloc] initWithCapacity:10]; - } - - prop = _propertyDict[aName]; - if (nil != prop) { - return prop; - } - - NSUInteger staticIdx = staticIndexWithName(aName); - - if (NSNotFound != staticIdx) { - POPStaticAnimatableProperty *staticProp = [[POPStaticAnimatableProperty alloc] init]; - staticProp->_state = &_staticStates[staticIdx]; - _propertyDict[aName] = staticProp; - prop = staticProp; - } else if (NULL != aBlock) { - POPMutableAnimatableProperty *mutableProp = [[POPMutableAnimatableProperty alloc] init]; - mutableProp.name = aName; - mutableProp.threshold = 1.0; - aBlock(mutableProp); - prop = [mutableProp copy]; - } - - return prop; -} - -- (NSString *)description -{ - NSMutableString *s = [NSMutableString stringWithFormat:@"%@ name:%@ threshold:%f", super.description, self.name, self.threshold]; - return s; -} - -@end diff --git a/Pods/pop/pop/POPAnimation.h b/Pods/pop/pop/POPAnimation.h deleted file mode 100644 index 3c710f2..0000000 --- a/Pods/pop/pop/POPAnimation.h +++ /dev/null @@ -1,188 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import -#import - -@class CAMediaTimingFunction; - -/** - @abstract The abstract animation base class. - @discussion Instantiate and use one of the concrete animation subclasses. - */ -@interface POPAnimation : NSObject - -/** - @abstract The name of the animation. - @discussion Optional property to help identify the animation. - */ -@property (copy, nonatomic) NSString *name; - -/** - @abstract The beginTime of the animation in media time. - @discussion Defaults to 0 and starts immediately. - */ -@property (assign, nonatomic) CFTimeInterval beginTime; - -/** - @abstract The animation delegate. - @discussion See {@ref POPAnimationDelegate} for details. - */ -@property (weak, nonatomic) id delegate; - -/** - @abstract The animation tracer. - @discussion Returns the existing tracer, creating one if needed. Call start/stop on the tracer to toggle event collection. - */ -@property (readonly, nonatomic) POPAnimationTracer *tracer; - -/** - @abstract Optional block called on animation start. - */ -@property (copy, nonatomic) void (^animationDidStartBlock)(POPAnimation *anim); - -/** - @abstract Optional block called when value meets or exceeds to value. - */ -@property (copy, nonatomic) void (^animationDidReachToValueBlock)(POPAnimation *anim); - -/** - @abstract Optional block called on animation completion. - */ -@property (copy, nonatomic) void (^completionBlock)(POPAnimation *anim, BOOL finished); - -/** - @abstract Optional block called each frame animation is applied. - */ -@property (copy, nonatomic) void (^animationDidApplyBlock)(POPAnimation *anim); - -/** - @abstract Flag indicating whether animation should be removed on completion. - @discussion Setting to NO can facilitate animation reuse. Defaults to YES. - */ -@property (assign, nonatomic) BOOL removedOnCompletion; - -/** - @abstract Flag indicating whether animation is paused. - @discussion A paused animation is excluded from the list of active animations. On initial creation, defaults to YES. On animation addition, the animation is implicity unpaused. On animation completion, the animation is implicity paused including for animations with removedOnCompletion set to NO. - */ -@property (assign, nonatomic, getter = isPaused) BOOL paused; - -/** - @abstract Flag indicating whether animation autoreverses. - @discussion An animation that autoreverses will have twice the duration before it is considered finished. It will animate to the toValue, stop, then animate back to the original fromValue. The delegate methods are called as follows: - - 1) animationDidStart: is called at the beginning, as usual, and then after each toValue is reached and the autoreverse is going to start. - 2) animationDidReachToValue: is called every time the toValue is reached. The toValue is swapped with the fromValue at the end of each animation segment. This means that with autoreverses set to YES, the animationDidReachToValue: delegate method will be called a minimum of twice. - 3) animationDidStop:finished: is called every time the toValue is reached, the finished argument will be NO if the autoreverse is not yet complete. - */ -@property (assign, nonatomic) BOOL autoreverses; - -/** - @abstract The number of times to repeat the animation. - @discussion A repeatCount of 0 or 1 means that the animation will not repeat, just like Core Animation. A repeatCount of 2 or greater means that the animation will run that many times before stopping. The delegate methods are called as follows: - - 1) animationDidStart: is called at the beginning of each animation repeat. - 2) animationDidReachToValue: is called every time the toValue is reached. - 3) animationDidStop:finished: is called every time the toValue is reached, the finished argument will be NO if the autoreverse is not yet complete. - -When combined with the autoreverses property, a singular animation is effectively twice as long. - */ -@property (assign, nonatomic) NSInteger repeatCount; - -/** - @abstract Repeat the animation forever. - @discussion This property will make the animation repeat forever. The value of the repeatCount property is undefined when this property is set. The finished parameter of the delegate callback animationDidStop:finished: will always be NO. - */ -@property (assign, nonatomic) BOOL repeatForever; - -@end - -/** - @abstract The animation delegate. - */ -@protocol POPAnimationDelegate -@optional - -/** - @abstract Called on animation start. - @param anim The relevant animation. - */ -- (void)pop_animationDidStart:(POPAnimation *)anim; - -/** - @abstract Called when value meets or exceeds to value. - @param anim The relevant animation. - */ -- (void)pop_animationDidReachToValue:(POPAnimation *)anim; - -/** - @abstract Called on animation stop. - @param anim The relevant animation. - @param finished Flag indicating finished state. Flag is true if the animation reached completion before being removed. - */ -- (void)pop_animationDidStop:(POPAnimation *)anim finished:(BOOL)finished; - -/** - @abstract Called each frame animation is applied. - @param anim The relevant animation. - */ -- (void)pop_animationDidApply:(POPAnimation *)anim; - -@end - - -@interface NSObject (POP) - -/** - @abstract Add an animation to the reciver. - @param anim The animation to add. - @param key The key used to identify the animation. - @discussion The 'key' may be any string such that only one animation per unique key is added per object. - */ -- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key; - -/** - @abstract Remove all animations attached to the receiver. - */ -- (void)pop_removeAllAnimations; - -/** - @abstract Remove any animation attached to the receiver for 'key'. - @param key The key used to identify the animation. - */ -- (void)pop_removeAnimationForKey:(NSString *)key; - -/** - @abstract Returns an array containing the keys of all animations currently attached to the receiver. - @param The order of keys reflects the order in which animations will be applied. - */ -- (NSArray *)pop_animationKeys; - -/** - @abstract Returns any animation attached to the receiver. - @param key The key used to identify the animation. - @returns The animation currently attached, or nil if no such animation exists. - */ -- (id)pop_animationForKey:(NSString *)key; - -@end - -/** - * This implementation of NSCopying does not do any copying of animation's state, but only configuration. - * i.e. you cannot copy an animation and expect to apply it to a view and have the copied animation pick up where the original left off. - * Two common uses of copying animations: - * * you need to apply the same animation to multiple different views. - * * you need to absolutely ensure that the the caller of your function cannot mutate the animation once it's been passed in. - */ -@interface POPAnimation (NSCopying) - -@end diff --git a/Pods/pop/pop/POPAnimation.mm b/Pods/pop/pop/POPAnimation.mm deleted file mode 100644 index 75bdeb1..0000000 --- a/Pods/pop/pop/POPAnimation.mm +++ /dev/null @@ -1,303 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPAnimationExtras.h" -#import "POPAnimationInternal.h" - -#import - -#import "POPAction.h" -#import "POPAnimationRuntime.h" -#import "POPAnimationTracerInternal.h" -#import "POPAnimatorPrivate.h" - -using namespace POP; - -#pragma mark - POPAnimation - -@implementation POPAnimation -@synthesize solver = _solver; -@synthesize currentValue = _currentValue; -@synthesize progressMarkers = _progressMarkers; - -#pragma mark - Lifecycle - -- (id)init -{ - [NSException raise:NSStringFromClass([self class]) format:@"Attempting to instantiate an abstract class. Use a concrete subclass instead."]; - return nil; -} - -- (id)_init -{ - self = [super init]; - if (nil != self) { - [self _initState]; - } - return self; -} - -- (void)_initState -{ - _state = new POPAnimationState(self); -} - -- (void)dealloc -{ - if (_state) { - delete _state; - _state = NULL; - }; -} - -#pragma mark - Properties - -- (id)delegate -{ - return _state->delegate; -} - -- (void)setDelegate:(id)delegate -{ - _state->setDelegate(delegate); -} - -- (BOOL)isPaused -{ - return _state->paused; -} - -- (void)setPaused:(BOOL)paused -{ - _state->setPaused(paused ? true : false); -} - -- (NSInteger)repeatCount -{ - if (_state->autoreverses) { - return _state->repeatCount / 2; - } else { - return _state->repeatCount; - } -} - -- (void)setRepeatCount:(NSInteger)repeatCount -{ - if (repeatCount > 0) { - if (repeatCount > NSIntegerMax / 2) { - repeatCount = NSIntegerMax / 2; - } - - if (_state->autoreverses) { - _state->repeatCount = (repeatCount * 2); - } else { - _state->repeatCount = repeatCount; - } - } -} - -- (BOOL)autoreverses -{ - return _state->autoreverses; -} - -- (void)setAutoreverses:(BOOL)autoreverses -{ - _state->autoreverses = autoreverses; - if (autoreverses) { - if (_state->repeatCount == 0) { - [self setRepeatCount:1]; - } - } -} - -FB_PROPERTY_GET(POPAnimationState, type, POPAnimationType); -DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, animationDidStartBlock, setAnimationDidStartBlock:, POPAnimationDidStartBlock); -DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, animationDidReachToValueBlock, setAnimationDidReachToValueBlock:, POPAnimationDidReachToValueBlock); -DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, completionBlock, setCompletionBlock:, POPAnimationCompletionBlock); -DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, animationDidApplyBlock, setAnimationDidApplyBlock:, POPAnimationDidApplyBlock); -DEFINE_RW_PROPERTY_OBJ_COPY(POPAnimationState, name, setName:, NSString*); -DEFINE_RW_PROPERTY(POPAnimationState, beginTime, setBeginTime:, CFTimeInterval); -DEFINE_RW_FLAG(POPAnimationState, removedOnCompletion, removedOnCompletion, setRemovedOnCompletion:); -DEFINE_RW_FLAG(POPAnimationState, repeatForever, repeatForever, setRepeatForever:); - -- (id)valueForUndefinedKey:(NSString *)key -{ - return _state->dict[key]; -} - -- (void)setValue:(id)value forUndefinedKey:(NSString *)key -{ - if (!value) { - [_state->dict removeObjectForKey:key]; - } else { - if (!_state->dict) - _state->dict = [[NSMutableDictionary alloc] init]; - _state->dict[key] = value; - } -} - -- (POPAnimationTracer *)tracer -{ - if (!_state->tracer) { - _state->tracer = [[POPAnimationTracer alloc] initWithAnimation:self]; - } - return _state->tracer; -} - -- (NSString *)description -{ - NSMutableString *s = [NSMutableString stringWithFormat:@"<%@:%p", NSStringFromClass([self class]), self]; - [self _appendDescription:s debug:NO]; - [s appendString:@">"]; - return s; -} - -- (NSString *)debugDescription -{ - NSMutableString *s = [NSMutableString stringWithFormat:@"<%@:%p", NSStringFromClass([self class]), self]; - [self _appendDescription:s debug:YES]; - [s appendString:@">"]; - return s; -} - -#pragma mark - Utility - -POPAnimationState *POPAnimationGetState(POPAnimation *a) -{ - return a->_state; -} - -- (BOOL)_advance:(id)object currentTime:(CFTimeInterval)currentTime elapsedTime:(CFTimeInterval)elapsedTime -{ - return YES; -} - -- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug -{ - if (_state->name) - [s appendFormat:@"; name = %@", _state->name]; - - if (!self.removedOnCompletion) - [s appendFormat:@"; removedOnCompletion = %@", POPStringFromBOOL(self.removedOnCompletion)]; - - if (debug) { - if (_state->active) - [s appendFormat:@"; active = %@", POPStringFromBOOL(_state->active)]; - - if (_state->paused) - [s appendFormat:@"; paused = %@", POPStringFromBOOL(_state->paused)]; - } - - if (_state->beginTime) { - [s appendFormat:@"; beginTime = %f", _state->beginTime]; - } - - for (NSString *key in _state->dict) { - [s appendFormat:@"; %@ = %@", key, _state->dict[key]]; - } -} - -@end - - -#pragma mark - POPPropertyAnimation - -#pragma mark - POPBasicAnimation - -#pragma mark - POPDecayAnimation - -@implementation NSObject (POP) - -- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key -{ - [[POPAnimator sharedAnimator] addAnimation:anim forObject:self key:key]; -} - -- (void)pop_removeAllAnimations -{ - [[POPAnimator sharedAnimator] removeAllAnimationsForObject:self]; -} - -- (void)pop_removeAnimationForKey:(NSString *)key -{ - [[POPAnimator sharedAnimator] removeAnimationForObject:self key:key]; -} - -- (NSArray *)pop_animationKeys -{ - return [[POPAnimator sharedAnimator] animationKeysForObject:self]; -} - -- (id)pop_animationForKey:(NSString *)key -{ - return [[POPAnimator sharedAnimator] animationForObject:self key:key]; -} - -@end - -@implementation NSProxy (POP) - -- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key -{ - [[POPAnimator sharedAnimator] addAnimation:anim forObject:self key:key]; -} - -- (void)pop_removeAllAnimations -{ - [[POPAnimator sharedAnimator] removeAllAnimationsForObject:self]; -} - -- (void)pop_removeAnimationForKey:(NSString *)key -{ - [[POPAnimator sharedAnimator] removeAnimationForObject:self key:key]; -} - -- (NSArray *)pop_animationKeys -{ - return [[POPAnimator sharedAnimator] animationKeysForObject:self]; -} - -- (id)pop_animationForKey:(NSString *)key -{ - return [[POPAnimator sharedAnimator] animationForObject:self key:key]; -} - -@end - -@implementation POPAnimation (NSCopying) - -- (instancetype)copyWithZone:(NSZone *)zone -{ - /* - * Must use [self class] instead of POPAnimation so that subclasses can call this via super. - * Even though POPAnimation and POPPropertyAnimation throw exceptions on init, - * it's safe to call it since you can only copy objects that have been successfully created. - */ - POPAnimation *copy = [[[self class] allocWithZone:zone] init]; - - if (copy) { - copy.name = self.name; - copy.beginTime = self.beginTime; - copy.delegate = self.delegate; - copy.animationDidStartBlock = self.animationDidStartBlock; - copy.animationDidReachToValueBlock = self.animationDidReachToValueBlock; - copy.completionBlock = self.completionBlock; - copy.animationDidApplyBlock = self.animationDidApplyBlock; - copy.removedOnCompletion = self.removedOnCompletion; - - copy.autoreverses = self.autoreverses; - copy.repeatCount = self.repeatCount; - copy.repeatForever = self.repeatForever; - } - - return copy; -} - -@end \ No newline at end of file diff --git a/Pods/pop/pop/POPAnimationEvent.h b/Pods/pop/pop/POPAnimationEvent.h deleted file mode 100644 index e761091..0000000 --- a/Pods/pop/pop/POPAnimationEvent.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -/** - @abstract Enumeraton of animation event types. - */ -typedef NS_ENUM(NSUInteger, POPAnimationEventType) { - kPOPAnimationEventPropertyRead = 0, - kPOPAnimationEventPropertyWrite, - kPOPAnimationEventToValueUpdate, - kPOPAnimationEventFromValueUpdate, - kPOPAnimationEventVelocityUpdate, - kPOPAnimationEventBouncinessUpdate, - kPOPAnimationEventSpeedUpdate, - kPOPAnimationEventFrictionUpdate, - kPOPAnimationEventMassUpdate, - kPOPAnimationEventTensionUpdate, - kPOPAnimationEventDidStart, - kPOPAnimationEventDidStop, - kPOPAnimationEventDidReachToValue, - kPOPAnimationEventAutoreversed -}; - -/** - @abstract The base animation event class. - */ -@interface POPAnimationEvent : NSObject - -/** - @abstract The event type. See {@ref POPAnimationEventType} for possible values. - */ -@property (readonly, nonatomic, assign) POPAnimationEventType type; - -/** - @abstract The time of event. - */ -@property (readonly, nonatomic, assign) CFTimeInterval time; - -/** - @abstract Optional string describing the animation at time of event. - */ -@property (readonly, nonatomic, copy) NSString *animationDescription; - -@end - -/** - @abstract An animation event subclass for recording value and velocity. - */ -@interface POPAnimationValueEvent : POPAnimationEvent - -/** - @abstract The value recorded. - */ -@property (readonly, nonatomic, strong) id value; - -/** - @abstract The velocity recorded, if any. - */ -@property (readonly, nonatomic, strong) id velocity; - -@end diff --git a/Pods/pop/pop/POPAnimationEvent.mm b/Pods/pop/pop/POPAnimationEvent.mm deleted file mode 100644 index d3a13b6..0000000 --- a/Pods/pop/pop/POPAnimationEvent.mm +++ /dev/null @@ -1,108 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPAnimationEvent.h" -#import "POPAnimationEventInternal.h" - -static NSString *stringFromType(POPAnimationEventType aType) -{ - switch (aType) { - case kPOPAnimationEventPropertyRead: - return @"read"; - case kPOPAnimationEventPropertyWrite: - return @"write"; - case kPOPAnimationEventToValueUpdate: - return @"toValue"; - case kPOPAnimationEventFromValueUpdate: - return @"fromValue"; - case kPOPAnimationEventVelocityUpdate: - return @"velocity"; - case kPOPAnimationEventSpeedUpdate: - return @"speed"; - case kPOPAnimationEventBouncinessUpdate: - return @"bounciness"; - case kPOPAnimationEventFrictionUpdate: - return @"friction"; - case kPOPAnimationEventMassUpdate: - return @"mass"; - case kPOPAnimationEventTensionUpdate: - return @"tension"; - case kPOPAnimationEventDidStart: - return @"didStart"; - case kPOPAnimationEventDidStop: - return @"didStop"; - case kPOPAnimationEventDidReachToValue: - return @"didReachToValue"; - case kPOPAnimationEventAutoreversed: - return @"autoreversed"; - default: - return nil; - } -} - -@implementation POPAnimationEvent -@synthesize type = _type; -@synthesize time = _time; -@synthesize animationDescription = _animationDescription; - -- (instancetype)initWithType:(POPAnimationEventType)aType time:(CFTimeInterval)aTime -{ - self = [super init]; - if (nil != self) { - _type = aType; - _time = aTime; - } - return self; -} - -- (NSString *)description -{ - NSMutableString *s = [NSMutableString stringWithFormat:@""]; - return s; -} - -// subclass override -- (void)_appendDescription:(NSMutableString *)s -{ - if (0 != _animationDescription.length) { - [s appendFormat:@"; animation = %@", _animationDescription]; - } -} - -@end - -@implementation POPAnimationValueEvent -@synthesize value = _value; -@synthesize velocity = _velocity; - -- (instancetype)initWithType:(POPAnimationEventType)aType time:(CFTimeInterval)aTime value:(id)aValue -{ - self = [self initWithType:aType time:aTime]; - if (nil != self) { - _value = aValue; - } - return self; -} - -- (void)_appendDescription:(NSMutableString *)s -{ - [super _appendDescription:s]; - - if (nil != _value) { - [s appendFormat:@"; value = %@", _value]; - } - - if (nil != _velocity) { - [s appendFormat:@"; velocity = %@", _velocity]; - } -} - -@end diff --git a/Pods/pop/pop/POPAnimationEventInternal.h b/Pods/pop/pop/POPAnimationEventInternal.h deleted file mode 100644 index 398d59b..0000000 --- a/Pods/pop/pop/POPAnimationEventInternal.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import "POPAnimationEvent.h" - -@interface POPAnimationEvent () - -/** - @abstract Default initializer. - */ -- (instancetype)initWithType:(POPAnimationEventType)type time:(CFTimeInterval)time; - -/** - @abstract Readwrite redefinition of public property. - */ -@property (readwrite, nonatomic, copy) NSString *animationDescription; - -@end - -@interface POPAnimationValueEvent () - -/** - @abstract Default initializer. - */ -- (instancetype)initWithType:(POPAnimationEventType)type time:(CFTimeInterval)time value:(id)value; - -/** - @abstract Readwrite redefinition of public property. - */ -@property (readwrite, nonatomic, strong) id velocity; - -@end - diff --git a/Pods/pop/pop/POPAnimationExtras.h b/Pods/pop/pop/POPAnimationExtras.h deleted file mode 100644 index 4b3d237..0000000 --- a/Pods/pop/pop/POPAnimationExtras.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import -#import - -/** - @abstract The current drag coefficient. - @discussion A value greater than 1.0 indicates Simulator slow-motion animations are enabled. Defaults to 1.0. - */ -extern CGFloat POPAnimationDragCoefficient(); - -@interface CAAnimation (POPAnimationExtras) - -/** - @abstract Apply the current drag coefficient to animation speed. - @discussion Convenience utility to respect Simulator slow-motion animation settings. - */ -- (void)pop_applyDragCoefficient; - -@end - -@interface POPSpringAnimation (POPAnimationExtras) - -/** - @abstract Converts from spring bounciness and speed to tension, friction and mass dynamics values. - */ -+ (void)convertBounciness:(CGFloat)bounciness speed:(CGFloat)speed toTension:(CGFloat *)outTension friction:(CGFloat *)outFriction mass:(CGFloat *)outMass; - -/** - @abstract Converts from dynamics tension, friction and mass to spring bounciness and speed values. - */ -+ (void)convertTension:(CGFloat)tension friction:(CGFloat)friction toBounciness:(CGFloat *)outBounciness speed:(CGFloat *)outSpeed; - -@end diff --git a/Pods/pop/pop/POPAnimationExtras.mm b/Pods/pop/pop/POPAnimationExtras.mm deleted file mode 100644 index 0a6d6c9..0000000 --- a/Pods/pop/pop/POPAnimationExtras.mm +++ /dev/null @@ -1,117 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPAnimationExtras.h" -#import "POPAnimationPrivate.h" - -#if TARGET_OS_IPHONE -#import -#endif - -#if TARGET_IPHONE_SIMULATOR -UIKIT_EXTERN float UIAnimationDragCoefficient(); // UIKit private drag coeffient, use judiciously -#endif - -#import "POPMath.h" - -CGFloat POPAnimationDragCoefficient() -{ -#if TARGET_IPHONE_SIMULATOR - return UIAnimationDragCoefficient(); -#else - return 1.0; -#endif -} - -@implementation CAAnimation (POPAnimationExtras) - -- (void)pop_applyDragCoefficient -{ - CGFloat k = POPAnimationDragCoefficient(); - if (k != 0 && k != 1) - self.speed = 1 / k; -} - -@end - -@implementation POPSpringAnimation (POPAnimationExtras) - -static const CGFloat POPBouncy3NormalizationRange = 20.0; -static const CGFloat POPBouncy3NormalizationScale = 1.7; -static const CGFloat POPBouncy3BouncinessNormalizedMin = 0.0; -static const CGFloat POPBouncy3BouncinessNormalizedMax = 0.8; -static const CGFloat POPBouncy3SpeedNormalizedMin = 0.5; -static const CGFloat POPBouncy3SpeedNormalizedMax = 200; -static const CGFloat POPBouncy3FrictionInterpolationMax = 0.01; - -+ (void)convertBounciness:(CGFloat)bounciness speed:(CGFloat)speed toTension:(CGFloat *)outTension friction:(CGFloat *)outFriction mass:(CGFloat *)outMass -{ - double b = POPNormalize(bounciness / POPBouncy3NormalizationScale, 0, POPBouncy3NormalizationRange); - b = POPProjectNormal(b, POPBouncy3BouncinessNormalizedMin, POPBouncy3BouncinessNormalizedMax); - - double s = POPNormalize(speed / POPBouncy3NormalizationScale, 0, POPBouncy3NormalizationRange); - - CGFloat tension = POPProjectNormal(s, POPBouncy3SpeedNormalizedMin, POPBouncy3SpeedNormalizedMax); - CGFloat friction = POPQuadraticOutInterpolation(b, POPBouncy3NoBounce(tension), POPBouncy3FrictionInterpolationMax); - - tension = POP_ANIMATION_TENSION_FOR_QC_TENSION(tension); - friction = POP_ANIMATION_FRICTION_FOR_QC_FRICTION(friction); - - if (outTension) { - *outTension = tension; - } - - if (outFriction) { - *outFriction = friction; - } - - if (outMass) { - *outMass = 1.0; - } -} - -+ (void)convertTension:(CGFloat)tension friction:(CGFloat)friction toBounciness:(CGFloat *)outBounciness speed:(CGFloat *)outSpeed -{ - // Convert to QC values, in which our calculations are done. - CGFloat qcFriction = QC_FRICTION_FOR_POP_ANIMATION_FRICTION(friction); - CGFloat qcTension = QC_TENSION_FOR_POP_ANIMATION_TENSION(tension); - - // Friction is a function of bounciness and tension, according to the following: - // friction = POPQuadraticOutInterpolation(b, POPBouncy3NoBounce(tension), POPBouncy3FrictionInterpolationMax); - // Solve for bounciness, given a tension and friction. - - CGFloat nobounceTension = POPBouncy3NoBounce(qcTension); - CGFloat bounciness1, bounciness2; - - POPQuadraticSolve((nobounceTension - POPBouncy3FrictionInterpolationMax), // a - 2 * (POPBouncy3FrictionInterpolationMax - nobounceTension), // b - (nobounceTension - qcFriction), // c - bounciness1, // x1 - bounciness2); // x2 - - - // Choose the quadratic solution within the normalized bounciness range - CGFloat projectedNormalizedBounciness = (bounciness2 < POPBouncy3BouncinessNormalizedMax) ? bounciness2 : bounciness1; - CGFloat projectedNormalizedSpeed = qcTension; - - // Reverse projection + normalization - CGFloat bounciness = ((POPBouncy3NormalizationRange * POPBouncy3NormalizationScale) / (POPBouncy3BouncinessNormalizedMax - POPBouncy3BouncinessNormalizedMin)) * (projectedNormalizedBounciness - POPBouncy3BouncinessNormalizedMin); - CGFloat speed = ((POPBouncy3NormalizationRange * POPBouncy3NormalizationScale) / (POPBouncy3SpeedNormalizedMax - POPBouncy3SpeedNormalizedMin)) * (projectedNormalizedSpeed - POPBouncy3SpeedNormalizedMin); - - // Write back results - if (outBounciness) { - *outBounciness = bounciness; - } - - if (outSpeed) { - *outSpeed = speed; - } -} - -@end diff --git a/Pods/pop/pop/POPAnimationInternal.h b/Pods/pop/pop/POPAnimationInternal.h deleted file mode 100644 index 317364f..0000000 --- a/Pods/pop/pop/POPAnimationInternal.h +++ /dev/null @@ -1,505 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPAnimation.h" - -#import - -#import "POPAction.h" -#import "POPAnimationRuntime.h" -#import "POPAnimationTracerInternal.h" -#import "POPSpringSolver.h" - -using namespace POP; - -/** - Enumeration of supported animation types. - */ -enum POPAnimationType -{ - kPOPAnimationSpring, - kPOPAnimationDecay, - kPOPAnimationBasic, - kPOPAnimationCustom, -}; - -typedef struct -{ - CGFloat progress; - bool reached; -} POPProgressMarker; - -typedef void (^POPAnimationDidStartBlock)(POPAnimation *anim); -typedef void (^POPAnimationDidReachToValueBlock)(POPAnimation *anim); -typedef void (^POPAnimationCompletionBlock)(POPAnimation *anim, BOOL finished); -typedef void (^POPAnimationDidApplyBlock)(POPAnimation *anim); - -@interface POPAnimation() -- (instancetype)_init; - -@property (assign, nonatomic) SpringSolver4d *solver; -@property (readonly, nonatomic) POPAnimationType type; - -/** - The current animation value, updated while animation is progressing. - */ -@property (copy, nonatomic, readonly) id currentValue; - -/** - An array of optional progress markers. For each marker specified, the animation delegate will be informed when progress meets or exceeds the value specified. Specifying values outside of the [0, 1] range will give undefined results. - */ -@property (copy, nonatomic) NSArray *progressMarkers; - -/** - Return YES to indicate animation should continue animating. - */ -- (BOOL)_advance:(id)object currentTime:(CFTimeInterval)currentTime elapsedTime:(CFTimeInterval)elapsedTime; - -/** - Subclass override point to append animation description. - */ -- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug; - -@end - -NS_INLINE NSString *describe(VectorConstRef vec) -{ - return NULL == vec ? @"null" : vec->toString(); -} - -NS_INLINE Vector4r vector4(VectorConstRef vec) -{ - return NULL == vec ? Vector4r::Zero() : vec->vector4r(); -} - -NS_INLINE Vector4d vector4d(VectorConstRef vec) -{ - if (NULL == vec) { - return Vector4d::Zero(); - } else { - return vec->vector4r().cast(); - } -} - -NS_INLINE bool vec_equal(VectorConstRef v1, VectorConstRef v2) -{ - if (v1 == v2) { - return true; - } - if (!v1 || !v2) { - return false; - } - return *v1 == *v2; -} - -NS_INLINE CGFloat * vec_data(VectorRef vec) -{ - return NULL == vec ? NULL : vec->data(); -} - -template -struct ComputeProgressFunctor { - CGFloat operator()(const T &value, const T &start, const T &end) const { - return 0; - } -}; - -template<> -struct ComputeProgressFunctor { - CGFloat operator()(const Vector4r &value, const Vector4r &start, const Vector4r &end) const { - CGFloat s = (value - start).squaredNorm(); // distance from start - CGFloat e = (value - end).squaredNorm(); // distance from end - CGFloat d = (end - start).squaredNorm(); // distance from start to end - - if (0 == d) { - return 1; - } else if (s > e) { - // s -------- p ---- e OR s ------- e ---- p - return sqrtr(s/d); - } else { - // s --- p --------- e OR p ---- s ------- e - return 1 - sqrtr(e/d); - } - } -}; - -struct _POPAnimationState; -struct _POPDecayAnimationState; -struct _POPPropertyAnimationState; - -extern _POPAnimationState *POPAnimationGetState(POPAnimation *a); - - -#define FB_FLAG_GET(stype, flag, getter) \ -- (BOOL)getter { \ - return ((stype *)_state)->flag; \ -} - -#define FB_FLAG_SET(stype, flag, mutator) \ -- (void)mutator (BOOL)value { \ - if (value == ((stype *)_state)->flag) \ - return; \ - ((stype *)_state)->flag = value; \ -} - -#define DEFINE_RW_FLAG(stype, flag, getter, mutator) \ - FB_FLAG_GET (stype, flag, getter) \ - FB_FLAG_SET (stype, flag, mutator) - -#define FB_PROPERTY_GET(stype, property, ctype) \ -- (ctype)property { \ - return ((stype *)_state)->property; \ -} - -#define FB_PROPERTY_SET(stype, property, mutator, ctype, ...) \ -- (void)mutator (ctype)value { \ - if (value == ((stype *)_state)->property) \ - return; \ - ((stype *)_state)->property = value; \ - __VA_ARGS__ \ -} - -#define FB_PROPERTY_SET_OBJ_COPY(stype, property, mutator, ctype, ...) \ -- (void)mutator (ctype)value { \ - if (value == ((stype *)_state)->property) \ - return; \ - ((stype *)_state)->property = [value copy]; \ - __VA_ARGS__ \ -} - -#define DEFINE_RW_PROPERTY(stype, flag, mutator, ctype, ...) \ - FB_PROPERTY_GET (stype, flag, ctype) \ - FB_PROPERTY_SET (stype, flag, mutator, ctype, __VA_ARGS__) - -#define DEFINE_RW_PROPERTY_OBJ(stype, flag, mutator, ctype, ...) \ - FB_PROPERTY_GET (stype, flag, ctype) \ - FB_PROPERTY_SET (stype, flag, mutator, ctype, __VA_ARGS__) - -#define DEFINE_RW_PROPERTY_OBJ_COPY(stype, flag, mutator, ctype, ...) \ - FB_PROPERTY_GET (stype, flag, ctype) \ - FB_PROPERTY_SET_OBJ_COPY (stype, flag, mutator, ctype, __VA_ARGS__) - - -/** - Internal delegate definition. - */ -@interface NSObject (POPAnimationDelegateInternal) -- (void)pop_animation:(POPAnimation *)anim didReachProgress:(CGFloat)progress; -@end - -struct _POPAnimationState -{ - id __unsafe_unretained self; - POPAnimationType type; - NSString *name; - NSUInteger ID; - CFTimeInterval beginTime; - CFTimeInterval startTime; - CFTimeInterval lastTime; - id __weak delegate; - POPAnimationDidStartBlock animationDidStartBlock; - POPAnimationDidReachToValueBlock animationDidReachToValueBlock; - POPAnimationCompletionBlock completionBlock; - POPAnimationDidApplyBlock animationDidApplyBlock; - NSMutableDictionary *dict; - POPAnimationTracer *tracer; - CGFloat progress; - NSInteger repeatCount; - - bool active:1; - bool paused:1; - bool removedOnCompletion:1; - - bool delegateDidStart:1; - bool delegateDidStop:1; - bool delegateDidProgress:1; - bool delegateDidApply:1; - bool delegateDidReachToValue:1; - - bool additive:1; - bool didReachToValue:1; - bool tracing:1; // corresponds to tracer started - bool userSpecifiedDynamics:1; - bool autoreverses:1; - bool repeatForever:1; - bool customFinished:1; - - _POPAnimationState(id __unsafe_unretained anim) : - self(anim), - type((POPAnimationType)0), - name(nil), - ID(0), - beginTime(0), - startTime(0), - lastTime(0), - delegate(nil), - animationDidStartBlock(nil), - animationDidReachToValueBlock(nil), - completionBlock(nil), - animationDidApplyBlock(nil), - dict(nil), - tracer(nil), - progress(0), - repeatCount(0), - active(false), - paused(true), - removedOnCompletion(true), - delegateDidStart(false), - delegateDidStop(false), - delegateDidProgress(false), - delegateDidApply(false), - delegateDidReachToValue(false), - additive(false), - didReachToValue(false), - tracing(false), - userSpecifiedDynamics(false), - autoreverses(false), - repeatForever(false), - customFinished(false) {} - - virtual ~_POPAnimationState() - { - name = nil; - dict = nil; - tracer = nil; - animationDidStartBlock = NULL; - animationDidReachToValueBlock = NULL; - completionBlock = NULL; - animationDidApplyBlock = NULL; - } - - bool isCustom() { - return kPOPAnimationCustom == type; - } - - bool isStarted() { - return 0 != startTime; - } - - id getDelegate() { - return delegate; - } - - void setDelegate(id d) { - if (d != delegate) { - delegate = d; - delegateDidStart = [d respondsToSelector:@selector(pop_animationDidStart:)]; - delegateDidStop = [d respondsToSelector:@selector(pop_animationDidStop:finished:)]; - delegateDidProgress = [d respondsToSelector:@selector(pop_animation:didReachProgress:)]; - delegateDidApply = [d respondsToSelector:@selector(pop_animationDidApply:)]; - delegateDidReachToValue = [d respondsToSelector:@selector(pop_animationDidReachToValue:)]; - } - } - - bool getPaused() { - return paused; - } - - void setPaused(bool f) { - if (f != paused) { - paused = f; - if (!paused) { - reset(false); - } - } - } - - CGFloat getProgress() { - return progress; - } - - /* returns true if started */ - bool startIfNeeded(id obj, CFTimeInterval time, CFTimeInterval offset) - { - bool started = false; - - // detect start based on time - if (0 == startTime && time >= beginTime + offset) { - - // activate & unpause - active = true; - setPaused(false); - - // note start time - startTime = lastTime = time; - started = true; - } - - // ensure values for running animation - bool running = active && !paused; - if (running) { - willRun(started, obj); - } - - // handle start - if (started) { - handleDidStart(); - } - - return started; - } - - void stop(bool removing, bool done) { - if (active) - { - // delegate progress one last time - if (done) { - delegateProgress(); - } - - if (removing) { - active = false; - } - - handleDidStop(done); - } else { - - // stopped before even started - // delegate start and stop regardless; matches CA behavior - if (!isStarted()) { - handleDidStart(); - handleDidStop(false); - } - } - - setPaused(true); - } - - virtual void handleDidStart() - { - if (delegateDidStart) { - ActionEnabler enabler; - [delegate pop_animationDidStart:self]; - } - - POPAnimationDidStartBlock block = animationDidStartBlock; - if (block != NULL) { - ActionEnabler enabler; - block(self); - } - - if (tracing) { - [tracer didStart]; - } - } - - void handleDidStop(BOOL done) - { - if (delegateDidStop) { - ActionEnabler enabler; - [delegate pop_animationDidStop:self finished:done]; - } - - // add another strong reference to completion block before callout - POPAnimationCompletionBlock block = completionBlock; - if (block != NULL) { - ActionEnabler enabler; - block(self, done); - } - - if (tracing) { - [tracer didStop:done]; - } - } - - /* virtual functions */ - virtual bool isDone() { - if (isCustom()) { - return customFinished; - } - - return false; - } - - bool advanceTime(CFTimeInterval time, id obj) { - bool advanced = false; - bool computedProgress = false; - CFTimeInterval dt = time - lastTime; - - switch (type) { - case kPOPAnimationSpring: - advanced = advance(time, dt, obj); - break; - case kPOPAnimationDecay: - advanced = advance(time, dt, obj); - break; - case kPOPAnimationBasic: { - advanced = advance(time, dt, obj); - computedProgress = true; - break; - } - case kPOPAnimationCustom: { - customFinished = [self _advance:obj currentTime:time elapsedTime:dt] ? false : true; - advanced = true; - break; - } - default: - break; - } - - if (advanced) { - - // estimate progress - if (!computedProgress) { - computeProgress(); - } - - // delegate progress - delegateProgress(); - - // update time - lastTime = time; - } - - return advanced; - } - - virtual void willRun(bool started, id obj) {} - virtual bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) { return false; } - virtual void computeProgress() {} - virtual void delegateProgress() {} - - virtual void delegateApply() { - if (delegateDidApply) { - ActionEnabler enabler; - [delegate pop_animationDidApply:self]; - } - - POPAnimationDidApplyBlock block = animationDidApplyBlock; - if (block != NULL) { - ActionEnabler enabler; - block(self); - } - } - - virtual void reset(bool all) { - startTime = 0; - lastTime = 0; - } -}; - -typedef struct _POPAnimationState POPAnimationState; - - -@interface POPAnimation () -{ -@protected - struct _POPAnimationState *_state; -} - -@end - -// NSProxy extensions, for testing pursposes -@interface NSProxy (POP) -- (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key; -- (void)pop_removeAllAnimations; -- (void)pop_removeAnimationForKey:(NSString *)key; -- (NSArray *)pop_animationKeys; -- (POPAnimation *)pop_animationForKey:(NSString *)key; -@end diff --git a/Pods/pop/pop/POPAnimationPrivate.h b/Pods/pop/pop/POPAnimationPrivate.h deleted file mode 100644 index c0f06c5..0000000 --- a/Pods/pop/pop/POPAnimationPrivate.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#define POP_ANIMATION_FRICTION_FOR_QC_FRICTION(qcFriction) (25.0 + (((qcFriction - 8.0) / 2.0) * (25.0 - 19.0))) -#define POP_ANIMATION_TENSION_FOR_QC_TENSION(qcTension) (194.0 + (((qcTension - 30.0) / 50.0) * (375.0 - 194.0))) - -#define QC_FRICTION_FOR_POP_ANIMATION_FRICTION(fbFriction) (8.0 + 2.0 * ((fbFriction - 25.0)/(25.0 - 19.0))) -#define QC_TENSION_FOR_POP_ANIMATION_TENSION(fbTension) (30.0 + 50.0 * ((fbTension - 194.0)/(375.0 - 194.0))) diff --git a/Pods/pop/pop/POPAnimationRuntime.h b/Pods/pop/pop/POPAnimationRuntime.h deleted file mode 100644 index 902c312..0000000 --- a/Pods/pop/pop/POPAnimationRuntime.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import - -#import "POPVector.h" - -enum POPValueType -{ - kPOPValueUnknown = 0, - kPOPValueInteger, - kPOPValueFloat, - kPOPValuePoint, - kPOPValueSize, - kPOPValueRect, - kPOPValueEdgeInsets, - kPOPValueAffineTransform, - kPOPValueTransform, - kPOPValueRange, - kPOPValueColor, - kPOPValueSCNVector3, - kPOPValueSCNVector4, -}; - -using namespace POP; - -/** - Returns value type based on objc type description, given list of supported value types and length. - */ -extern POPValueType POPSelectValueType(const char *objctype, const POPValueType *types, size_t length); - -/** - Returns value type based on objc object, given a list of supported value types and length. - */ -extern POPValueType POPSelectValueType(id obj, const POPValueType *types, size_t length); - -/** - Array of all value types. - */ -extern const POPValueType kPOPAnimatableAllTypes[12]; - -/** - Array of all value types supported for animation. - */ -extern const POPValueType kPOPAnimatableSupportTypes[10]; - -/** - Returns a string description of a value type. - */ -extern NSString *POPValueTypeToString(POPValueType t); - -/** - Returns a mutable dictionary of weak pointer keys to weak pointer values. - */ -extern CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToWeakPointer(NSUInteger capacity) CF_RETURNS_RETAINED; - -/** - Returns a mutable dictionary of weak pointer keys to weak pointer values. - */ -extern CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToStrongObject(NSUInteger capacity) CF_RETURNS_RETAINED; - -/** - Box a vector. - */ -extern id POPBox(VectorConstRef vec, POPValueType type, bool force = false); - -/** - Unbox a vector. - */ -extern VectorRef POPUnbox(id value, POPValueType &type, NSUInteger &count, bool validate); - -/** - Read/write block typedefs for convenience. - */ -typedef void(^pop_animatable_read_block)(id obj, CGFloat *value); -typedef void(^pop_animatable_write_block)(id obj, const CGFloat *value); - -/** - Read object value and return a Vector4r. - */ -NS_INLINE Vector4r read_values(pop_animatable_read_block read, id obj, size_t count) -{ - Vector4r vec = Vector4r::Zero(); - if (0 == count) - return vec; - - read(obj, vec.data()); - - return vec; -} - -NS_INLINE NSString *POPStringFromBOOL(BOOL value) -{ - return value ? @"YES" : @"NO"; -} diff --git a/Pods/pop/pop/POPAnimationRuntime.mm b/Pods/pop/pop/POPAnimationRuntime.mm deleted file mode 100644 index 371e009..0000000 --- a/Pods/pop/pop/POPAnimationRuntime.mm +++ /dev/null @@ -1,329 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPAnimationRuntime.h" - -#import - -#import - -#if TARGET_OS_IPHONE -#import -#endif - -#import "POPCGUtils.h" -#import "POPDefines.h" -#import "POPGeometry.h" -#import "POPVector.h" - -static Boolean pointerEqual(const void *ptr1, const void *ptr2) { - return ptr1 == ptr2; -} - -static CFHashCode pointerHash(const void *ptr) { - return (CFHashCode)(ptr); -} - -CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToWeakPointer(NSUInteger capacity) -{ - CFDictionaryKeyCallBacks kcb = kCFTypeDictionaryKeyCallBacks; - - // weak, pointer keys - kcb.retain = NULL; - kcb.release = NULL; - kcb.equal = pointerEqual; - kcb.hash = pointerHash; - - CFDictionaryValueCallBacks vcb = kCFTypeDictionaryValueCallBacks; - - // weak, pointer values - vcb.retain = NULL; - vcb.release = NULL; - vcb.equal = pointerEqual; - - return CFDictionaryCreateMutable(NULL, capacity, &kcb, &vcb); -} - -CFMutableDictionaryRef POPDictionaryCreateMutableWeakPointerToStrongObject(NSUInteger capacity) -{ - CFDictionaryKeyCallBacks kcb = kCFTypeDictionaryKeyCallBacks; - - // weak, pointer keys - kcb.retain = NULL; - kcb.release = NULL; - kcb.equal = pointerEqual; - kcb.hash = pointerHash; - - // strong, object values - CFDictionaryValueCallBacks vcb = kCFTypeDictionaryValueCallBacks; - - return CFDictionaryCreateMutable(NULL, capacity, &kcb, &vcb); -} - -static bool FBCompareTypeEncoding(const char *objctype, POPValueType type) -{ - switch (type) - { - case kPOPValueFloat: - return (strcmp(objctype, @encode(float)) == 0 - || strcmp(objctype, @encode(double)) == 0 - ); - - case kPOPValuePoint: - return (strcmp(objctype, @encode(CGPoint)) == 0 -#if !TARGET_OS_IPHONE - || strcmp(objctype, @encode(NSPoint)) == 0 -#endif - ); - - case kPOPValueSize: - return (strcmp(objctype, @encode(CGSize)) == 0 -#if !TARGET_OS_IPHONE - || strcmp(objctype, @encode(NSSize)) == 0 -#endif - ); - - case kPOPValueRect: - return (strcmp(objctype, @encode(CGRect)) == 0 -#if !TARGET_OS_IPHONE - || strcmp(objctype, @encode(NSRect)) == 0 -#endif - ); - case kPOPValueEdgeInsets: -#if TARGET_OS_IPHONE - return strcmp(objctype, @encode(UIEdgeInsets)) == 0; -#else - return false; -#endif - - case kPOPValueAffineTransform: - return strcmp(objctype, @encode(CGAffineTransform)) == 0; - - case kPOPValueTransform: - return strcmp(objctype, @encode(CATransform3D)) == 0; - - case kPOPValueRange: - return strcmp(objctype, @encode(CFRange)) == 0 - || strcmp(objctype, @encode (NSRange)) == 0; - - case kPOPValueInteger: - return (strcmp(objctype, @encode(int)) == 0 - || strcmp(objctype, @encode(unsigned int)) == 0 - || strcmp(objctype, @encode(short)) == 0 - || strcmp(objctype, @encode(unsigned short)) == 0 - || strcmp(objctype, @encode(long)) == 0 - || strcmp(objctype, @encode(unsigned long)) == 0 - || strcmp(objctype, @encode(long long)) == 0 - || strcmp(objctype, @encode(unsigned long long)) == 0 - ); - - case kPOPValueSCNVector3: -#if SCENEKIT_SDK_AVAILABLE - return strcmp(objctype, @encode(SCNVector3)) == 0; -#else - return false; -#endif - - case kPOPValueSCNVector4: -#if SCENEKIT_SDK_AVAILABLE - return strcmp(objctype, @encode(SCNVector4)) == 0; -#else - return false; -#endif - - default: - return false; - } -} - -POPValueType POPSelectValueType(const char *objctype, const POPValueType *types, size_t length) -{ - if (NULL != objctype) { - for (size_t idx = 0; idx < length; idx++) { - if (FBCompareTypeEncoding(objctype, types[idx])) - return types[idx]; - } - } - return kPOPValueUnknown; -} - -POPValueType POPSelectValueType(id obj, const POPValueType *types, size_t length) -{ - if ([obj isKindOfClass:[NSValue class]]) { - return POPSelectValueType([obj objCType], types, length); - } else if (NULL != POPCGColorWithColor(obj)) { - return kPOPValueColor; - } - return kPOPValueUnknown; -} - -const POPValueType kPOPAnimatableAllTypes[12] = {kPOPValueInteger, kPOPValueFloat, kPOPValuePoint, kPOPValueSize, kPOPValueRect, kPOPValueEdgeInsets, kPOPValueAffineTransform, kPOPValueTransform, kPOPValueRange, kPOPValueColor, kPOPValueSCNVector3, kPOPValueSCNVector4}; - -const POPValueType kPOPAnimatableSupportTypes[10] = {kPOPValueInteger, kPOPValueFloat, kPOPValuePoint, kPOPValueSize, kPOPValueRect, kPOPValueEdgeInsets, kPOPValueColor, kPOPValueSCNVector3, kPOPValueSCNVector4}; - -NSString *POPValueTypeToString(POPValueType t) -{ - switch (t) { - case kPOPValueUnknown: - return @"unknown"; - case kPOPValueInteger: - return @"int"; - case kPOPValueFloat: - return @"CGFloat"; - case kPOPValuePoint: - return @"CGPoint"; - case kPOPValueSize: - return @"CGSize"; - case kPOPValueRect: - return @"CGRect"; - case kPOPValueEdgeInsets: - return @"UIEdgeInsets"; - case kPOPValueAffineTransform: - return @"CGAffineTransform"; - case kPOPValueTransform: - return @"CATransform3D"; - case kPOPValueRange: - return @"CFRange"; - case kPOPValueColor: - return @"CGColorRef"; - case kPOPValueSCNVector3: - return @"SCNVector3"; - case kPOPValueSCNVector4: - return @"SCNVector4"; - default: - return nil; - } -} - -id POPBox(VectorConstRef vec, POPValueType type, bool force) -{ - if (NULL == vec) - return nil; - - switch (type) { - case kPOPValueInteger: - case kPOPValueFloat: - return @(vec->data()[0]); - break; - case kPOPValuePoint: - return [NSValue valueWithCGPoint:vec->cg_point()]; - break; - case kPOPValueSize: - return [NSValue valueWithCGSize:vec->cg_size()]; - break; - case kPOPValueRect: - return [NSValue valueWithCGRect:vec->cg_rect()]; - break; -#if TARGET_OS_IPHONE - case kPOPValueEdgeInsets: - return [NSValue valueWithUIEdgeInsets:vec->ui_edge_insets()]; - break; -#endif - case kPOPValueColor: { - return (__bridge_transfer id)vec->cg_color(); - break; - } -#if SCENEKIT_SDK_AVAILABLE - case kPOPValueSCNVector3: { - return [NSValue valueWithSCNVector3:vec->scn_vector3()]; - break; - } - case kPOPValueSCNVector4: { - return [NSValue valueWithSCNVector4:vec->scn_vector4()]; - break; - } -#endif - default: - return force ? [NSValue valueWithCGPoint:vec->cg_point()] : nil; - break; - } -} - -static VectorRef vectorize(id value, POPValueType type) -{ - Vector *vec = NULL; - - switch (type) { - case kPOPValueInteger: - case kPOPValueFloat: -#if CGFLOAT_IS_DOUBLE - vec = Vector::new_cg_float([value doubleValue]); -#else - vec = Vector::new_cg_float([value floatValue]); -#endif - break; - case kPOPValuePoint: - vec = Vector::new_cg_point([value CGPointValue]); - break; - case kPOPValueSize: - vec = Vector::new_cg_size([value CGSizeValue]); - break; - case kPOPValueRect: - vec = Vector::new_cg_rect([value CGRectValue]); - break; -#if TARGET_OS_IPHONE - case kPOPValueEdgeInsets: - vec = Vector::new_ui_edge_insets([value UIEdgeInsetsValue]); - break; -#endif - case kPOPValueAffineTransform: - vec = Vector::new_cg_affine_transform([value CGAffineTransformValue]); - break; - case kPOPValueColor: - vec = Vector::new_cg_color(POPCGColorWithColor(value)); - break; -#if SCENEKIT_SDK_AVAILABLE - case kPOPValueSCNVector3: - vec = Vector::new_scn_vector3([value SCNVector3Value]); - break; - case kPOPValueSCNVector4: - vec = Vector::new_scn_vector4([value SCNVector4Value]); - break; -#endif - default: - break; - } - - return VectorRef(vec); -} - -VectorRef POPUnbox(id value, POPValueType &animationType, NSUInteger &count, bool validate) -{ - if (nil == value) { - count = 0; - return VectorRef(NULL); - } - - // determine type of value - POPValueType valueType = POPSelectValueType(value, kPOPAnimatableSupportTypes, POP_ARRAY_COUNT(kPOPAnimatableSupportTypes)); - - // handle unknown types - if (kPOPValueUnknown == valueType) { - NSString *valueDesc = [[value class] description]; - [NSException raise:@"Unsuported value" format:@"Animating %@ values is not supported", valueDesc]; - } - - // vectorize - VectorRef vec = vectorize(value, valueType); - - if (kPOPValueUnknown == animationType || 0 == count) { - // update animation type based on value type - animationType = valueType; - if (NULL != vec) { - count = vec->size(); - } - } else if (validate) { - // allow for mismatched types, so long as vector size matches - if (count != vec->size()) { - [NSException raise:@"Invalid value" format:@"%@ should be of type %@", value, POPValueTypeToString(animationType)]; - } - } - - return vec; -} diff --git a/Pods/pop/pop/POPAnimationTracer.h b/Pods/pop/pop/POPAnimationTracer.h deleted file mode 100644 index 72b26c3..0000000 --- a/Pods/pop/pop/POPAnimationTracer.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import - -@class POPAnimation; - -/** - @abstract Tracer of animation events to fasciliate unit testing & debugging. - */ -@interface POPAnimationTracer : NSObject - -/** - @abstract Start recording events. - */ -- (void)start; - -/** - @abstract Stop recording events. - */ -- (void)stop; - -/** - @abstract Resets any recoded events. Continues recording events if already started. - */ -- (void)reset; - -/** - @abstract Property representing all recorded events. - @discussion Events are returned in order of occurence. - */ -@property (nonatomic, assign, readonly) NSArray *allEvents; - -/** - @abstract Property representing all recorded write events for convenience. - @discussion Events are returned in order of occurence. - */ -@property (nonatomic, assign, readonly) NSArray *writeEvents; - -/** - @abstract Queries for events of specified type. - @param type The type of event to return. - @returns An array of events of specified type in order of occurence. - */ -- (NSArray *)eventsWithType:(POPAnimationEventType)type; - -/** - @abstract Property indicating whether tracer should automatically log events and reset collection on animation completion. - */ -@property (nonatomic, assign) BOOL shouldLogAndResetOnCompletion; - -@end diff --git a/Pods/pop/pop/POPAnimationTracer.mm b/Pods/pop/pop/POPAnimationTracer.mm deleted file mode 100644 index 243fb9b..0000000 --- a/Pods/pop/pop/POPAnimationTracer.mm +++ /dev/null @@ -1,191 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPAnimationTracer.h" - -#import - -#import "POPAnimationEventInternal.h" -#import "POPAnimationInternal.h" -#import "POPSpringAnimation.h" - -@implementation POPAnimationTracer -{ - __weak POPAnimation *_animation; - POPAnimationState *_animationState; - NSMutableArray *_events; - BOOL _animationHasVelocity; -} -@synthesize shouldLogAndResetOnCompletion = _shouldLogAndResetOnCompletion; - -static POPAnimationEvent *create_event(POPAnimationTracer *self, POPAnimationEventType type, id value = nil, bool recordAnimation = false) -{ - bool useLocalTime = 0 != self->_animationState->startTime; - CFTimeInterval time = useLocalTime - ? self->_animationState->lastTime - self->_animationState->startTime - : self->_animationState->lastTime; - - POPAnimationEvent *event; - - if (!value) { - event = [[POPAnimationEvent alloc] initWithType:type time:time]; - } else { - event = [[POPAnimationValueEvent alloc] initWithType:type time:time value:value]; - if (self->_animationHasVelocity) { - [(POPAnimationValueEvent *)event setVelocity:[(POPSpringAnimation *)self->_animation velocity]]; - } - } - - if (recordAnimation) { - event.animationDescription = [self->_animation description]; - } - - return event; -} - -- (id)initWithAnimation:(POPAnimation *)anAnim -{ - self = [super init]; - if (nil != self) { - _animation = anAnim; - _animationState = POPAnimationGetState(anAnim); - _events = [[NSMutableArray alloc] initWithCapacity:50]; - _animationHasVelocity = [anAnim respondsToSelector:@selector(velocity)]; - } - return self; -} - -- (void)readPropertyValue:(id)aValue -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventPropertyRead, aValue); - [_events addObject:event]; -} - -- (void)writePropertyValue:(id)aValue -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventPropertyWrite, aValue); - [_events addObject:event]; -} - -- (void)updateToValue:(id)aValue -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventToValueUpdate, aValue); - [_events addObject:event]; -} - -- (void)updateFromValue:(id)aValue -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventFromValueUpdate, aValue); - [_events addObject:event]; -} - -- (void)updateVelocity:(id)aValue -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventVelocityUpdate, aValue); - [_events addObject:event]; -} - -- (void)updateSpeed:(float)aFloat -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventSpeedUpdate, @(aFloat)); - [_events addObject:event]; -} - -- (void)updateBounciness:(float)aFloat -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventBouncinessUpdate, @(aFloat)); - [_events addObject:event]; -} - -- (void)updateFriction:(float)aFloat -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventFrictionUpdate, @(aFloat)); - [_events addObject:event]; -} - -- (void)updateMass:(float)aFloat -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventMassUpdate, @(aFloat)); - [_events addObject:event]; -} - -- (void)updateTension:(float)aFloat -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventTensionUpdate, @(aFloat)); - [_events addObject:event]; -} - -- (void)didStart -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventDidStart, nil, true); - [_events addObject:event]; -} - -- (void)didStop:(BOOL)finished -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventDidStop, @(finished), true); - [_events addObject:event]; - - if (_shouldLogAndResetOnCompletion) { - NSLog(@"events:%@", self.allEvents); - [self reset]; - } -} - -- (void)didReachToValue:(id)aValue -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventDidReachToValue, aValue); - [_events addObject:event]; -} - -- (void)autoreversed -{ - POPAnimationEvent *event = create_event(self, kPOPAnimationEventAutoreversed); - [_events addObject:event]; -} - -- (void)start -{ - POPAnimationState *s = POPAnimationGetState(_animation); - s->tracing = true; -} - -- (void)stop -{ - POPAnimationState *s = POPAnimationGetState(_animation); - s->tracing = false; -} - -- (void)reset -{ - [_events removeAllObjects]; -} - -- (NSArray *)allEvents -{ - return [_events copy]; -} - -- (NSArray *)writeEvents -{ - return [self eventsWithType:kPOPAnimationEventPropertyWrite]; -} - -- (NSArray *)eventsWithType:(POPAnimationEventType)aType -{ - NSMutableArray *array = [NSMutableArray array]; - for (POPAnimationEvent *event in _events) { - if (aType == event.type) { - [array addObject:event]; - } - } - return array; -} - -@end diff --git a/Pods/pop/pop/POPAnimationTracerInternal.h b/Pods/pop/pop/POPAnimationTracerInternal.h deleted file mode 100644 index 00958e1..0000000 --- a/Pods/pop/pop/POPAnimationTracerInternal.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import - -@interface POPAnimationTracer (Internal) - -/** - @abstract Designated initalizer. Pass the animation being traced. - */ -- (instancetype)initWithAnimation:(POPAnimation *)anAnim; - -/** - @abstract Records read value. - */ -- (void)readPropertyValue:(id)aValue; - -/** - @abstract Records write value. - */ -- (void)writePropertyValue:(id)aValue; - -/** - Records to value update. - */ -- (void)updateToValue:(id)aValue; - -/** - @abstract Records from value update. - */ -- (void)updateFromValue:(id)aValue; - -/** - @abstract Records from value update. - */ -- (void)updateVelocity:(id)aValue; - -/** - @abstract Records bounciness update. - */ -- (void)updateBounciness:(float)aFloat; - -/** - @abstract Records speed update. - */ -- (void)updateSpeed:(float)aFloat; - -/** - @abstract Records friction update. - */ -- (void)updateFriction:(float)aFloat; - -/** - @abstract Records mass update. - */ -- (void)updateMass:(float)aFloat; - -/** - @abstract Records tension update. - */ -- (void)updateTension:(float)aFloat; - -/** - @abstract Records did add. - */ -- (void)didAdd; - -/** - @abstract Records did start. - */ -- (void)didStart; - -/** - @abstract Records did stop. - */ -- (void)didStop:(BOOL)finished; - -/** - @abstract Records did reach to value. - */ -- (void)didReachToValue:(id)aValue; - -/** - @abstract Records when an autoreverse animation takes place. - */ -- (void)autoreversed; - -@end diff --git a/Pods/pop/pop/POPAnimator.h b/Pods/pop/pop/POPAnimator.h deleted file mode 100644 index 5310bed..0000000 --- a/Pods/pop/pop/POPAnimator.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -@protocol POPAnimatorDelegate; - -/** - @abstract The animator class renders animations. - */ -@interface POPAnimator : NSObject - -/** - @abstract The shared animator instance. - @discussion Consumers should generally use the shared instance in lieu of creating new instances. - */ -+ (instancetype)sharedAnimator; - -/** - @abstract The optional animator delegate. - */ -@property (weak, nonatomic) id delegate; - -@end - -/** - @abstract The animator delegate. - */ -@protocol POPAnimatorDelegate - -/** - @abstract Called on each frame before animation application. - */ -- (void)animatorWillAnimate:(POPAnimator *)animator; - -/** - @abstract Called on each frame after animation application. - */ -- (void)animatorDidAnimate:(POPAnimator *)animator; - -@end diff --git a/Pods/pop/pop/POPAnimator.mm b/Pods/pop/pop/POPAnimator.mm deleted file mode 100644 index 6b2a770..0000000 --- a/Pods/pop/pop/POPAnimator.mm +++ /dev/null @@ -1,806 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPAnimator.h" -#import "POPAnimatorPrivate.h" - -#import -#import - -#if !TARGET_OS_IPHONE -#import -#endif - -#import - -#import - -#import "POPAnimation.h" -#import "POPAnimationExtras.h" -#import "POPBasicAnimationInternal.h" -#import "POPDecayAnimation.h" - -using namespace std; -using namespace POP; - -#define ENABLE_LOGGING_DEBUG 0 -#define ENABLE_LOGGING_INFO 0 - -#if ENABLE_LOGGING_DEBUG -#define FBLogAnimDebug NSLog -#else -#define FBLogAnimDebug(...) -#endif - -#if ENABLE_LOGGING_INFO -#define FBLogAnimInfo NSLog -#else -#define FBLogAnimInfo(...) -#endif - -class POPAnimatorItem -{ -public: - id __weak object; - NSString *key; - POPAnimation *animation; - NSInteger refCount; - id __unsafe_unretained unretainedObject; - - POPAnimatorItem(id o, NSString *k, POPAnimation *a) POP_NOTHROW - { - object = o; - key = [k copy]; - animation = a; - refCount = 1; - unretainedObject = o; - } - - ~POPAnimatorItem() - { - } - - bool operator==(const POPAnimatorItem& o) const { - return unretainedObject == o.unretainedObject && animation == o.animation && [key isEqualToString:o.key]; - } - -}; - -typedef std::shared_ptr POPAnimatorItemRef; -typedef std::shared_ptr POPAnimatorItemConstRef; - -typedef std::list POPAnimatorItemList; -typedef POPAnimatorItemList::iterator POPAnimatorItemListIterator; -typedef POPAnimatorItemList::const_iterator POPAnimatorItemListConstIterator; - -static BOOL _disableBackgroundThread = YES; - -@interface POPAnimator () -{ -#if TARGET_OS_IPHONE - CADisplayLink *_displayLink; -#else - CVDisplayLinkRef _displayLink; - int32_t _enqueuedRender; -#endif - POPAnimatorItemList _list; - CFMutableDictionaryRef _dict; - NSMutableArray *_observers; - POPAnimatorItemList _pendingList; - CFRunLoopObserverRef _pendingListObserver; - CFTimeInterval _slowMotionStartTime; - CFTimeInterval _slowMotionLastTime; - CFTimeInterval _slowMotionAccumulator; - CFTimeInterval _beginTime; - OSSpinLock _lock; - BOOL _disableDisplayLink; -} -@end - -@implementation POPAnimator -@synthesize delegate = _delegate; -@synthesize disableDisplayLink = _disableDisplayLink; -@synthesize beginTime = _beginTime; - -#if !TARGET_OS_IPHONE -static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *context) -{ - if (_disableBackgroundThread) { - __unsafe_unretained POPAnimator *pa = (__bridge POPAnimator *)context; - int32_t* enqueuedRender = &pa->_enqueuedRender; - if (*enqueuedRender == 0) { - OSAtomicIncrement32(enqueuedRender); - dispatch_async(dispatch_get_main_queue(), ^{ - [(__bridge POPAnimator*)context render]; - OSAtomicDecrement32(enqueuedRender); - }); - } - } else { - [(__bridge POPAnimator*)context render]; - } - return kCVReturnSuccess; -} -#endif - -// call while holding lock -static void updateDisplayLink(POPAnimator *self) -{ - BOOL paused = (0 == self->_observers.count && self->_list.empty()) || self->_disableDisplayLink; - -#if TARGET_OS_IPHONE - if (paused != self->_displayLink.paused) { - FBLogAnimInfo(paused ? @"pausing display link" : @"unpausing display link"); - self->_displayLink.paused = paused; - } -#else - if (paused == CVDisplayLinkIsRunning(self->_displayLink)) { - FBLogAnimInfo(paused ? @"pausing display link" : @"unpausing display link"); - if (paused) { - CVDisplayLinkStop(self->_displayLink); - } else { - CVDisplayLinkStart(self->_displayLink); - } - } -#endif -} - -static void updateAnimatable(id obj, POPPropertyAnimationState *anim, bool shouldAvoidExtraneousWrite = false) -{ - // handle user-initiated stop or pause; hault animation - if (!anim->active || anim->paused) - return; - - if (anim->hasValue()) { - pop_animatable_write_block write = anim->property.writeBlock; - if (NULL == write) - return; - - // current animation value - VectorRef currentVec = anim->currentValue(); - - if (!anim->additive) { - - // if avoiding extraneous writes and we have a read block defined - if (shouldAvoidExtraneousWrite) { - - pop_animatable_read_block read = anim->property.readBlock; - if (read) { - // compare current animation value with object value - Vector4r currentValue = currentVec->vector4r(); - Vector4r objectValue = read_values(read, obj, anim->valueCount); - if (objectValue == currentValue) { - return; - } - } - } - - // update previous values; support animation convergence - anim->previous2Vec = anim->previousVec; - anim->previousVec = currentVec; - - // write value - write(obj, currentVec->data()); - if (anim->tracing) { - [anim->tracer writePropertyValue:POPBox(currentVec, anim->valueType, true)]; - } - } else { - pop_animatable_read_block read = anim->property.readBlock; - NSCAssert(read, @"additive requires an animatable property readBlock"); - if (NULL == read) { - return; - } - - // object value - Vector4r objectValue = read_values(read, obj, anim->valueCount); - - // current value - Vector4r currentValue = currentVec->vector4r(); - - // determine animation change - if (anim->previousVec) { - Vector4r previousValue = anim->previousVec->vector4r(); - currentValue -= previousValue; - } - - // avoid writing no change - if (shouldAvoidExtraneousWrite && currentValue == Vector4r::Zero()) { - return; - } - - // add to object value - currentValue += objectValue; - - // update previous values; support animation convergence - anim->previous2Vec = anim->previousVec; - anim->previousVec = currentVec; - - // write value - write(obj, currentValue.data()); - if (anim->tracing) { - [anim->tracer writePropertyValue:POPBox(currentVec, anim->valueType, true)]; - } - } - } -} - -static void applyAnimationTime(id obj, POPAnimationState *state, CFTimeInterval time) -{ - if (!state->advanceTime(time, obj)) { - return; - } - - POPPropertyAnimationState *ps = dynamic_cast(state); - if (NULL != ps) { - updateAnimatable(obj, ps); - } - - state->delegateApply(); -} - -static void applyAnimationToValue(id obj, POPAnimationState *state) -{ - POPPropertyAnimationState *ps = dynamic_cast(state); - - if (NULL != ps) { - - // finalize progress - ps->finalizeProgress(); - - // write to value, updating only if needed - updateAnimatable(obj, ps, true); - } - - state->delegateApply(); -} - -static POPAnimation *deleteDictEntry(POPAnimator *self, id __unsafe_unretained obj, NSString *key, BOOL cleanup = YES) -{ - POPAnimation *anim = nil; - - // lock - OSSpinLockLock(&self->_lock); - - NSMutableDictionary *keyAnimationsDict = (__bridge id)CFDictionaryGetValue(self->_dict, (__bridge void *)obj); - if (keyAnimationsDict) { - - anim = keyAnimationsDict[key]; - if (anim) { - - // remove key - [keyAnimationsDict removeObjectForKey:key]; - - // cleanup empty dictionaries - if (cleanup && 0 == keyAnimationsDict.count) { - CFDictionaryRemoveValue(self->_dict, (__bridge void *)obj); - } - } - } - - // unlock - OSSpinLockUnlock(&self->_lock); - return anim; -} - -static void stopAndCleanup(POPAnimator *self, POPAnimatorItemRef item, bool shouldRemove, bool finished) -{ - // remove - if (shouldRemove) { - deleteDictEntry(self, item->unretainedObject, item->key); - } - - // stop - POPAnimationState *state = POPAnimationGetState(item->animation); - state->stop(shouldRemove, finished); - - if (shouldRemove) { - // lock - OSSpinLockLock(&self->_lock); - - // find item in list - // may have already been removed on animationDidStop: - POPAnimatorItemListIterator find_iter = find(self->_list.begin(), self->_list.end(), item); - BOOL found = find_iter != self->_list.end(); - - if (found) { - self->_list.erase(find_iter); - } - - // unlock - OSSpinLockUnlock(&self->_lock); - } -} - -+ (id)sharedAnimator -{ - static POPAnimator* _animator = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _animator = [[POPAnimator alloc] init]; - }); - return _animator; -} - -+ (BOOL)disableBackgroundThread -{ - return _disableBackgroundThread; -} - -+ (void)setDisableBackgroundThread:(BOOL)flag -{ - _disableBackgroundThread = flag; -} - -#pragma mark - Lifecycle - -- (id)init -{ - self = [super init]; - if (nil == self) return nil; - -#if TARGET_OS_IPHONE - _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)]; - _displayLink.paused = YES; - [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; -#else - CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); - CVDisplayLinkSetOutputCallback(_displayLink, displayLinkCallback, (__bridge void *)self); -#endif - - _dict = POPDictionaryCreateMutableWeakPointerToStrongObject(5); - _lock = OS_SPINLOCK_INIT; - - return self; -} - -- (void)dealloc -{ -#if TARGET_OS_IPHONE - [_displayLink invalidate]; -#else - CVDisplayLinkStop(_displayLink); - CVDisplayLinkRelease(_displayLink); -#endif - [self _clearPendingListObserver]; -} - -#pragma mark - Utility - -- (void)_processPendingList -{ - // rendering pending animations - CFTimeInterval time = [self _currentRenderTime]; - [self _renderTime:(0 != _beginTime) ? _beginTime : time items:_pendingList]; - - // lock - OSSpinLockLock(&_lock); - - // clear list and observer - _pendingList.clear(); - [self _clearPendingListObserver]; - - // unlock - OSSpinLockUnlock(&_lock); -} - -- (void)_clearPendingListObserver -{ - if (_pendingListObserver) { - CFRunLoopRemoveObserver(CFRunLoopGetMain(), _pendingListObserver, kCFRunLoopCommonModes); - CFRelease(_pendingListObserver); - _pendingListObserver = NULL; - } -} - -- (void)_scheduleProcessPendingList -{ - // see WebKit for magic numbers, eg http://trac.webkit.org/changeset/166540 - static const CFIndex CATransactionCommitRunLoopOrder = 2000000; - static const CFIndex POPAnimationApplyRunLoopOrder = CATransactionCommitRunLoopOrder - 1; - - // lock - OSSpinLockLock(&_lock); - - if (!_pendingListObserver) { - __weak POPAnimator *weakSelf = self; - - _pendingListObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopExit, false, POPAnimationApplyRunLoopOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { - [weakSelf _processPendingList]; - }); - - if (_pendingListObserver) { - CFRunLoopAddObserver(CFRunLoopGetMain(), _pendingListObserver, kCFRunLoopCommonModes); - } - } - - // unlock - OSSpinLockUnlock(&_lock); -} - -- (void)_renderTime:(CFTimeInterval)time items:(std::list)items -{ - // begin transaction with actions disabled - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - - // notify delegate - __strong __typeof__(_delegate) delegate = _delegate; - [delegate animatorWillAnimate:self]; - - // lock - OSSpinLockLock(&_lock); - - // count active animations - const NSUInteger count = items.size(); - if (0 == count) { - // unlock - OSSpinLockUnlock(&_lock); - } else { - // copy list into vector - std::vector vector{ items.begin(), items.end() }; - - // unlock - OSSpinLockUnlock(&_lock); - - for (auto item : vector) { - [self _renderTime:time item:item]; - } - } - - // notify observers - for (id observer in self.observers) { - [observer animatorDidAnimate:(id)self]; - } - - // lock - OSSpinLockLock(&_lock); - - // update display link - updateDisplayLink(self); - - // unlock - OSSpinLockUnlock(&_lock); - - // notify delegate and commit - [delegate animatorDidAnimate:self]; - [CATransaction commit]; -} - -- (void)_renderTime:(CFTimeInterval)time item:(POPAnimatorItemRef)item -{ - id obj = item->object; - POPAnimation *anim = item->animation; - POPAnimationState *state = POPAnimationGetState(anim); - - if (nil == obj) { - // object exists not; stop animating - NSAssert(item->unretainedObject, @"object should exist"); - stopAndCleanup(self, item, true, false); - } else { - - // start if needed - state->startIfNeeded(obj, time, _slowMotionAccumulator); - - // only run active, not paused animations - if (state->active && !state->paused) { - // object exists; animate - applyAnimationTime(obj, state, time); - - FBLogAnimDebug(@"time:%f running:%@", time, item->animation); - if (state->isDone()) { - // set end value - applyAnimationToValue(obj, state); - - state->repeatCount--; - if (state->repeatForever || state->repeatCount > 0) { - if ([anim isKindOfClass:[POPPropertyAnimation class]]) { - POPPropertyAnimation *propAnim = (POPPropertyAnimation *)anim; - id oldFromValue = propAnim.fromValue; - propAnim.fromValue = propAnim.toValue; - - if (state->autoreverses) { - if (state->tracing) { - [state->tracer autoreversed]; - } - - if (state->type == kPOPAnimationDecay) { - POPDecayAnimation *decayAnimation = (POPDecayAnimation *)propAnim; - decayAnimation.velocity = [decayAnimation reversedVelocity]; - } else { - propAnim.toValue = oldFromValue; - } - } else { - if (state->type == kPOPAnimationDecay) { - POPDecayAnimation *decayAnimation = (POPDecayAnimation *)propAnim; - id originalVelocity = decayAnimation.originalVelocity; - decayAnimation.velocity = originalVelocity; - } else { - propAnim.fromValue = oldFromValue; - } - } - } - - state->stop(NO, NO); - state->reset(true); - - state->startIfNeeded(obj, time, _slowMotionAccumulator); - } else { - stopAndCleanup(self, item, state->removedOnCompletion, YES); - } - } - } - } -} - -#pragma mark - API - -- (NSArray *)observers -{ - // lock - OSSpinLockLock(&_lock); - - // get observers - NSArray *observers = 0 != _observers.count ? [_observers copy] : nil; - - // unlock - OSSpinLockUnlock(&_lock); - return observers; -} - -- (void)addAnimation:(POPAnimation *)anim forObject:(id)obj key:(NSString *)key -{ - if (!anim || !obj) { - return; - } - - // support arbitrarily many nil keys - if (!key) { - key = [[NSUUID UUID] UUIDString]; - } - - // lock - OSSpinLockLock(&_lock); - - // get key, animation dict associated with object - NSMutableDictionary *keyAnimationDict = (__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj); - - // update associated animation state - if (nil == keyAnimationDict) { - keyAnimationDict = [NSMutableDictionary dictionary]; - CFDictionarySetValue(_dict, (__bridge void *)obj, (__bridge void *)keyAnimationDict); - } else { - // if the animation instance already exists, avoid cancelling only to restart - POPAnimation *existingAnim = keyAnimationDict[key]; - if (existingAnim) { - // unlock - OSSpinLockUnlock(&_lock); - - if (existingAnim == anim) { - return; - } - [self removeAnimationForObject:obj key:key cleanupDict:NO]; - - // lock - OSSpinLockLock(&_lock); - } - } - keyAnimationDict[key] = anim; - - // create entry after potential removal - POPAnimatorItemRef item(new POPAnimatorItem(obj, key, anim)); - - // add to list and pending list - _list.push_back(item); - _pendingList.push_back(item); - - // support animation re-use, reset all animation state - POPAnimationGetState(anim)->reset(true); - - // update display link - updateDisplayLink(self); - - // unlock - OSSpinLockUnlock(&_lock); - - // schedule runloop processing of pending animations - [self _scheduleProcessPendingList]; -} - -- (void)removeAllAnimationsForObject:(id)obj -{ - // lock - OSSpinLockLock(&_lock); - - NSArray *animations = [(__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj) allValues]; - CFDictionaryRemoveValue(_dict, (__bridge void *)obj); - - // unlock - OSSpinLockUnlock(&_lock); - - if (0 == animations.count) { - return; - } - - NSHashTable *animationSet = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:animations.count]; - for (id animation in animations) { - [animationSet addObject:animation]; - } - - // lock - OSSpinLockLock(&_lock); - - POPAnimatorItemRef item; - for (auto iter = _list.begin(); iter != _list.end();) { - item = *iter; - if(![animationSet containsObject:item->animation]) { - iter++; - } else { - iter = _list.erase(iter); - } - } - - // unlock - OSSpinLockUnlock(&_lock); - - for (POPAnimation *anim in animations) { - POPAnimationState *state = POPAnimationGetState(anim); - state->stop(true, !state->active); - } -} - -- (void)removeAnimationForObject:(id)obj key:(NSString *)key cleanupDict:(BOOL)cleanupDict -{ - POPAnimation *anim = deleteDictEntry(self, obj, key, cleanupDict); - if (nil == anim) { - return; - } - - // lock - OSSpinLockLock(&_lock); - - // remove from list - POPAnimatorItemRef item; - for (auto iter = _list.begin(); iter != _list.end();) { - item = *iter; - if(anim == item->animation) { - _list.erase(iter); - break; - } else { - iter++; - } - } - - // remove from pending list - for (auto iter = _pendingList.begin(); iter != _pendingList.end();) { - item = *iter; - if(anim == item->animation) { - _pendingList.erase(iter); - break; - } else { - iter++; - } - } - - // unlock - OSSpinLockUnlock(&_lock); - - // stop animation and callout - POPAnimationState *state = POPAnimationGetState(anim); - state->stop(true, (!state->active && !state->paused)); -} - -- (void)removeAnimationForObject:(id)obj key:(NSString *)key -{ - [self removeAnimationForObject:obj key:key cleanupDict:YES]; -} - -- (NSArray *)animationKeysForObject:(id)obj -{ - // lock - OSSpinLockLock(&_lock); - - // get keys - NSArray *keys = [(__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj) allKeys]; - - // unlock - OSSpinLockUnlock(&_lock); - return keys; -} - -- (id)animationForObject:(id)obj key:(NSString *)key -{ - // lock - OSSpinLockLock(&_lock); - - // lookup animation - NSDictionary *keyAnimationsDict = (__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj); - POPAnimation *animation = keyAnimationsDict[key]; - - // unlock - OSSpinLockUnlock(&_lock); - return animation; -} - -- (CFTimeInterval)_currentRenderTime -{ - CFTimeInterval time = CACurrentMediaTime(); - -#if TARGET_IPHONE_SIMULATOR - // support slow-motion animations - time += _slowMotionAccumulator; - float f = POPAnimationDragCoefficient(); - - if (f > 1.0) { - if (!_slowMotionStartTime) { - _slowMotionStartTime = time; - } else { - time = (time - _slowMotionStartTime) / f + _slowMotionStartTime; - _slowMotionLastTime = time; - } - } else if (_slowMotionStartTime) { - CFTimeInterval dt = (_slowMotionLastTime - time); - time += dt; - _slowMotionAccumulator += dt; - _slowMotionStartTime = 0; - } -#endif - - return time; -} - -- (void)render -{ - CFTimeInterval time = [self _currentRenderTime]; - [self renderTime:time]; -} - -- (void)renderTime:(CFTimeInterval)time -{ - [self _renderTime:time items:_list]; -} - -- (void)addObserver:(id)observer -{ - NSAssert(nil != observer, @"attempting to add nil %@ observer", self); - if (nil == observer) { - return; - } - - // lock - OSSpinLockLock(&_lock); - - if (!_observers) { - // use ordered collection for deterministic callout - _observers = [[NSMutableArray alloc] initWithCapacity:1]; - } - - [_observers addObject:observer]; - updateDisplayLink(self); - - // unlock - OSSpinLockUnlock(&_lock); -} - -- (void)removeObserver:(id)observer -{ - NSAssert(nil != observer, @"attempting to remove nil %@ observer", self); - if (nil == observer) { - return; - } - - // lock - OSSpinLockLock(&_lock); - - [_observers removeObject:observer]; - updateDisplayLink(self); - - // unlock - OSSpinLockUnlock(&_lock); -} - -@end diff --git a/Pods/pop/pop/POPAnimatorPrivate.h b/Pods/pop/pop/POPAnimatorPrivate.h deleted file mode 100644 index 5fba912..0000000 --- a/Pods/pop/pop/POPAnimatorPrivate.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -@class POPAnimation; - -@protocol POPAnimatorObserving -@required - -/** - @abstract Called on each observer after animator has advanced. Core Animation actions are disabled by default. - */ -- (void)animatorDidAnimate:(POPAnimator *)animator; - -@end - -@interface POPAnimator () - -#if !TARGET_OS_IPHONE -/** - Determines whether or not to use a high priority background thread for animation updates. Using a background thread can result in faster, more responsive updates, but may be less compatible. Defaults to YES. - */ -+ (BOOL)disableBackgroundThread; -+ (void)setDisableBackgroundThread:(BOOL)flag; -#endif - -/** - Used for externally driven animator instances. - */ -@property (assign, nonatomic) BOOL disableDisplayLink; - -/** - Time used when starting animations. Defaults to 0 meaning current media time is used. Exposed for unit testing. - */ -@property (assign, nonatomic) CFTimeInterval beginTime; - -/** - Exposed for unit testing. - */ -- (void)renderTime:(CFTimeInterval)time; - -/** - Funnel methods for category additions. - */ -- (void)addAnimation:(POPAnimation *)anim forObject:(id)obj key:(NSString *)key; -- (void)removeAllAnimationsForObject:(id)obj; -- (void)removeAnimationForObject:(id)obj key:(NSString *)key; -- (NSArray *)animationKeysForObject:(id)obj; -- (POPAnimation *)animationForObject:(id)obj key:(NSString *)key; - -/** - @abstract Add an animator observer. Observer will be notified of each subsequent animator advance until removal. - */ -- (void)addObserver:(id)observer; - -/** - @abstract Remove an animator observer. - */ -- (void)removeObserver:(id)observer; - -@end diff --git a/Pods/pop/pop/POPBasicAnimation.h b/Pods/pop/pop/POPBasicAnimation.h deleted file mode 100644 index ce2e23a..0000000 --- a/Pods/pop/pop/POPBasicAnimation.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -/** - @abstract A concrete basic animation class. - @discussion Animation is achieved through interpolation. - */ -@interface POPBasicAnimation : POPPropertyAnimation - -/** - @abstract The designated initializer. - @returns An instance of a basic animation. - */ -+ (instancetype)animation; - -/** - @abstract Convenience initializer that returns an animation with animatable property of name. - @param name The name of the animatable property. - @returns An instance of a basic animation configured with specified animatable property. - */ -+ (instancetype)animationWithPropertyNamed:(NSString *)name; - -/** - @abstract Convenience constructor. - @returns Returns a basic animation with kCAMediaTimingFunctionDefault timing function. - */ -+ (instancetype)defaultAnimation; - -/** - @abstract Convenience constructor. - @returns Returns a basic animation with kCAMediaTimingFunctionLinear timing function. - */ -+ (instancetype)linearAnimation; - -/** - @abstract Convenience constructor. - @returns Returns a basic animation with kCAMediaTimingFunctionEaseIn timing function. - */ -+ (instancetype)easeInAnimation; - -/** - @abstract Convenience constructor. - @returns Returns a basic animation with kCAMediaTimingFunctionEaseOut timing function. - */ -+ (instancetype)easeOutAnimation; - -/** - @abstract Convenience constructor. - @returns Returns a basic animation with kCAMediaTimingFunctionEaseInEaseOut timing function. - */ -+ (instancetype)easeInEaseOutAnimation; - -/** - @abstract The duration in seconds. Defaults to 0.4. - */ -@property (assign, nonatomic) CFTimeInterval duration; - -/** - @abstract A timing function defining the pacing of the animation. Defaults to nil indicating pacing according to kCAMediaTimingFunctionDefault. - */ -@property (strong, nonatomic) CAMediaTimingFunction *timingFunction; - -@end diff --git a/Pods/pop/pop/POPBasicAnimation.mm b/Pods/pop/pop/POPBasicAnimation.mm deleted file mode 100644 index 2843c99..0000000 --- a/Pods/pop/pop/POPBasicAnimation.mm +++ /dev/null @@ -1,106 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPBasicAnimationInternal.h" - -@implementation POPBasicAnimation - -#undef __state -#define __state ((POPBasicAnimationState *)_state) - -#pragma mark - Lifecycle - -+ (instancetype)animation -{ - return [[self alloc] init]; -} - -+ (instancetype)animationWithPropertyNamed:(NSString *)aName -{ - POPBasicAnimation *anim = [self animation]; - anim.property = [POPAnimatableProperty propertyWithName:aName]; - return anim; -} - -- (void)_initState -{ - _state = new POPBasicAnimationState(self); -} - -+ (instancetype)linearAnimation -{ - POPBasicAnimation *anim = [self animation]; - anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - return anim; -} - -+ (instancetype)easeInAnimation -{ - POPBasicAnimation *anim = [self animation]; - anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; - return anim; -} - -+ (instancetype)easeOutAnimation -{ - POPBasicAnimation *anim = [self animation]; - anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; - return anim; -} - -+ (instancetype)easeInEaseOutAnimation -{ - POPBasicAnimation *anim = [self animation]; - anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; - return anim; -} - -+ (instancetype)defaultAnimation -{ - POPBasicAnimation *anim = [self animation]; - anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]; - return anim; -} - -- (id)init -{ - return [self _init]; -} - -#pragma mark - Properties - -DEFINE_RW_PROPERTY(POPBasicAnimationState, duration, setDuration:, CFTimeInterval); -DEFINE_RW_PROPERTY_OBJ(POPBasicAnimationState, timingFunction, setTimingFunction:, CAMediaTimingFunction*, __state->updatedTimingFunction();); - -#pragma mark - Utility - -- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug -{ - [super _appendDescription:s debug:debug]; - if (__state->duration) - [s appendFormat:@"; duration = %f", __state->duration]; -} - -@end - -@implementation POPBasicAnimation (NSCopying) - -- (instancetype)copyWithZone:(NSZone *)zone { - - POPBasicAnimation *copy = [super copyWithZone:zone]; - - if (copy) { - copy.duration = self.duration; - copy.timingFunction = self.timingFunction; // not a 'copy', but timing functions are publicly immutable. - } - - return copy; -} - -@end \ No newline at end of file diff --git a/Pods/pop/pop/POPBasicAnimationInternal.h b/Pods/pop/pop/POPBasicAnimationInternal.h deleted file mode 100644 index 1027670..0000000 --- a/Pods/pop/pop/POPBasicAnimationInternal.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPBasicAnimation.h" - -#import "POPPropertyAnimationInternal.h" - -// default animation duration -static CGFloat const kPOPAnimationDurationDefault = 0.4; - -// progress threshold for computing done -static CGFloat const kPOPProgressThreshold = 1e-6; - -static void interpolate(POPValueType valueType, NSUInteger count, const CGFloat *fromVec, const CGFloat *toVec, CGFloat *outVec, CGFloat p) -{ - switch (valueType) { - case kPOPValueInteger: - case kPOPValueFloat: - case kPOPValuePoint: - case kPOPValueSize: - case kPOPValueRect: - case kPOPValueEdgeInsets: - case kPOPValueColor: - POPInterpolateVector(count, outVec, fromVec, toVec, p); - break; - default: - NSCAssert(false, @"unhandled type %d", valueType); - break; - } -} - -struct _POPBasicAnimationState : _POPPropertyAnimationState -{ - CAMediaTimingFunction *timingFunction; - double timingControlPoints[4]; - CFTimeInterval duration; - CFTimeInterval timeProgress; - - _POPBasicAnimationState(id __unsafe_unretained anim) : _POPPropertyAnimationState(anim), - timingFunction(nil), - timingControlPoints{0.}, - duration(kPOPAnimationDurationDefault), - timeProgress(0.) - { - type = kPOPAnimationBasic; - } - - bool isDone() { - if (_POPPropertyAnimationState::isDone()) { - return true; - } - return timeProgress + kPOPProgressThreshold >= 1.; - } - - void updatedTimingFunction() - { - float vec[4] = {0.}; - [timingFunction getControlPointAtIndex:1 values:&vec[0]]; - [timingFunction getControlPointAtIndex:2 values:&vec[2]]; - for (NSUInteger idx = 0; idx < POP_ARRAY_COUNT(vec); idx++) { - timingControlPoints[idx] = vec[idx]; - } - } - - bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) { - // default timing function - if (!timingFunction) { - ((POPBasicAnimation *)self).timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]; - } - - // solve for normalized time, aka progresss [0, 1] - CGFloat p = 1.0f; - if (duration > 0.0f) { - // cap local time to duration - CFTimeInterval t = MIN(time - startTime, duration) / duration; - p = POPTimingFunctionSolve(timingControlPoints, t, SOLVE_EPS(duration)); - timeProgress = t; - } else { - timeProgress = 1.; - } - - // interpolate and advance - interpolate(valueType, valueCount, fromVec->data(), toVec->data(), currentVec->data(), p); - progress = p; - clampCurrentValue(); - - return true; - } -}; - -typedef struct _POPBasicAnimationState POPBasicAnimationState; diff --git a/Pods/pop/pop/POPCGUtils.h b/Pods/pop/pop/POPCGUtils.h deleted file mode 100644 index c843947..0000000 --- a/Pods/pop/pop/POPCGUtils.h +++ /dev/null @@ -1,152 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#if TARGET_OS_IPHONE -#import -#else -#import -#endif - -#import "POPDefines.h" - -#if SCENEKIT_SDK_AVAILABLE -#import -#endif - -POP_EXTERN_C_BEGIN - -NS_INLINE CGPoint values_to_point(const CGFloat values[]) -{ - return CGPointMake(values[0], values[1]); -} - -NS_INLINE CGSize values_to_size(const CGFloat values[]) -{ - return CGSizeMake(values[0], values[1]); -} - -NS_INLINE CGRect values_to_rect(const CGFloat values[]) -{ - return CGRectMake(values[0], values[1], values[2], values[3]); -} - -#if SCENEKIT_SDK_AVAILABLE -NS_INLINE SCNVector3 values_to_vec3(const CGFloat values[]) -{ - return SCNVector3Make(values[0], values[1], values[2]); -} - -NS_INLINE SCNVector4 values_to_vec4(const CGFloat values[]) -{ - return SCNVector4Make(values[0], values[1], values[2], values[3]); -} -#endif - -#if TARGET_OS_IPHONE - -NS_INLINE UIEdgeInsets values_to_edge_insets(const CGFloat values[]) -{ - return UIEdgeInsetsMake(values[0], values[1], values[2], values[3]); -} - -#endif - -NS_INLINE void values_from_point(CGFloat values[], CGPoint p) -{ - values[0] = p.x; - values[1] = p.y; -} - -NS_INLINE void values_from_size(CGFloat values[], CGSize s) -{ - values[0] = s.width; - values[1] = s.height; -} - -NS_INLINE void values_from_rect(CGFloat values[], CGRect r) -{ - values[0] = r.origin.x; - values[1] = r.origin.y; - values[2] = r.size.width; - values[3] = r.size.height; -} - -#if SCENEKIT_SDK_AVAILABLE -NS_INLINE void values_from_vec3(CGFloat values[], SCNVector3 v) -{ - values[0] = v.x; - values[1] = v.y; - values[2] = v.z; -} - -NS_INLINE void values_from_vec4(CGFloat values[], SCNVector4 v) -{ - values[0] = v.x; - values[1] = v.y; - values[2] = v.z; - values[3] = v.w; -} -#endif - -#if TARGET_OS_IPHONE - -NS_INLINE void values_from_edge_insets(CGFloat values[], UIEdgeInsets i) -{ - values[0] = i.top; - values[1] = i.left; - values[2] = i.bottom; - values[3] = i.right; -} - -#endif - -/** - Takes a CGColorRef and converts it into RGBA components, if necessary. - */ -extern void POPCGColorGetRGBAComponents(CGColorRef color, CGFloat components[]); - -/** - Takes RGBA components and returns a CGColorRef. - */ -extern CGColorRef POPCGColorRGBACreate(const CGFloat components[]) CF_RETURNS_RETAINED; - -/** - Takes a color reference and returns a CGColor. - */ -extern CGColorRef POPCGColorWithColor(id color) CF_RETURNS_NOT_RETAINED; - -#if TARGET_OS_IPHONE - -/** - Takes a UIColor and converts it into RGBA components, if necessary. - */ -extern void POPUIColorGetRGBAComponents(UIColor *color, CGFloat components[]); - -/** - Takes RGBA components and returns a UIColor. - */ -extern UIColor *POPUIColorRGBACreate(const CGFloat components[]) NS_RETURNS_RETAINED; - -#else - -/** - Takes a NSColor and converts it into RGBA components, if necessary. - */ -extern void POPNSColorGetRGBAComponents(NSColor *color, CGFloat components[]); - -/** - Takes RGBA components and returns a NSColor. - */ -extern NSColor *POPNSColorRGBACreate(const CGFloat components[]) NS_RETURNS_RETAINED; - -#endif - -POP_EXTERN_C_END diff --git a/Pods/pop/pop/POPCGUtils.mm b/Pods/pop/pop/POPCGUtils.mm deleted file mode 100644 index 72a2d36..0000000 --- a/Pods/pop/pop/POPCGUtils.mm +++ /dev/null @@ -1,150 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPCGUtils.h" - -#import - -void POPCGColorGetRGBAComponents(CGColorRef color, CGFloat components[]) -{ - if (color) { - const CGFloat *colors = CGColorGetComponents(color); - size_t count = CGColorGetNumberOfComponents(color); - - if (4 == count) { - // RGB colorspace - components[0] = colors[0]; - components[1] = colors[1]; - components[2] = colors[2]; - components[3] = colors[3]; - } else if (2 == count) { - // Grey colorspace - components[0] = components[1] = components[2] = colors[0]; - components[3] = colors[1]; - } else { - // Use CI to convert - CIColor *ciColor = [CIColor colorWithCGColor:color]; - components[0] = ciColor.red; - components[1] = ciColor.green; - components[2] = ciColor.blue; - components[3] = ciColor.alpha; - } - } else { - memset(components, 0, 4 * sizeof(components[0])); - } -} - -CGColorRef POPCGColorRGBACreate(const CGFloat components[]) -{ -#if TARGET_OS_IPHONE - CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); - CGColorRef color = CGColorCreate(space, components); - CGColorSpaceRelease(space); - return color; -#else - return CGColorCreateGenericRGB(components[0], components[1], components[2], components[3]); -#endif -} - -CGColorRef POPCGColorWithColor(id color) -{ - if (CFGetTypeID((__bridge CFTypeRef)color) == CGColorGetTypeID()) { - return ((__bridge CGColorRef)color); - } -#if TARGET_OS_IPHONE - else if ([color isKindOfClass:[UIColor class]]) { - return [color CGColor]; - } -#else - else if ([color isKindOfClass:[NSColor class]]) { - // -[NSColor CGColor] is only supported since OSX 10.8+ - if ([color respondsToSelector:@selector(CGColor)]) { - return [color CGColor]; - } - - /* - * Otherwise create a CGColorRef manually. - * - * The original accessor is (or would be) declared as: - * @property(readonly) CGColorRef CGColor; - * - (CGColorRef)CGColor NS_RETURNS_INNER_POINTER CF_RETURNS_NOT_RETAINED; - * - * (Please note that OSX' accessor is atomic, while iOS' isn't.) - * - * The access to the NSColor object must thus be synchronized - * and the CGColorRef be stored as an associated object, - * to return a reference which doesn't need to be released manually. - */ - @synchronized(color) { - static const void* key = &key; - - CGColorRef colorRef = (__bridge CGColorRef)objc_getAssociatedObject(color, key); - - if (!colorRef) { - size_t numberOfComponents = [color numberOfComponents]; - CGFloat components[numberOfComponents]; - CGColorSpaceRef colorSpace = [[color colorSpace] CGColorSpace]; - - [color getComponents:components]; - - colorRef = CGColorCreate(colorSpace, components); - - objc_setAssociatedObject(color, key, (__bridge id)colorRef, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - CGColorRelease(colorRef); - } - - return colorRef; - } - } -#endif - return nil; -} - -#if TARGET_OS_IPHONE - -void POPUIColorGetRGBAComponents(UIColor *color, CGFloat components[]) -{ - return POPCGColorGetRGBAComponents(POPCGColorWithColor(color), components); -} - -UIColor *POPUIColorRGBACreate(const CGFloat components[]) -{ - CGColorRef colorRef = POPCGColorRGBACreate(components); - UIColor *color = [[UIColor alloc] initWithCGColor:colorRef]; - CGColorRelease(colorRef); - return color; -} - -#else - -void POPNSColorGetRGBAComponents(NSColor *color, CGFloat components[]) -{ - return POPCGColorGetRGBAComponents(POPCGColorWithColor(color), components); -} - -NSColor *POPNSColorRGBACreate(const CGFloat components[]) -{ - CGColorRef colorRef = POPCGColorRGBACreate(components); - NSColor *color = nil; - - if (colorRef) { - if ([NSColor respondsToSelector:@selector(colorWithCGColor:)]) { - color = [NSColor colorWithCGColor:colorRef]; - } else { - color = [NSColor colorWithCIColor:[CIColor colorWithCGColor:colorRef]]; - } - - CGColorRelease(colorRef); - } - - return color; -} - -#endif - diff --git a/Pods/pop/pop/POPCustomAnimation.h b/Pods/pop/pop/POPCustomAnimation.h deleted file mode 100644 index 501a755..0000000 --- a/Pods/pop/pop/POPCustomAnimation.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -@class POPCustomAnimation; - -/** - @abstract POPCustomAnimationBlock is the callback block of a custom animation. - @discussion This block will be executed for each animation frame and should update the property or properties being animated based on current timing. - @param target The object being animated. Reference the passed in target to help avoid retain loops. - @param animation The custom animation instance. Use to determine the current and elapsed time since last callback. Reference the passed in animation to help avoid retain loops. - @return Flag indicating whether the animation should continue animating. Return NO to indicate animation is done. - */ -typedef BOOL (^POPCustomAnimationBlock)(id target, POPCustomAnimation *animation); - -/** - @abstract POPCustomAnimation is a concrete animation subclass for custom animations. - */ -@interface POPCustomAnimation : POPAnimation - -/** -@abstract Creates and returns an initialized custom animation instance. -@discussion This is the designated initializer. -@param block The custom animation callback block. See {@ref POPCustomAnimationBlock}. -@return The initialized custom animation instance. -*/ -+ (instancetype)animationWithBlock:(POPCustomAnimationBlock)block; - -/** - @abstract The current animation time at time of callback. - */ -@property (readonly, nonatomic) CFTimeInterval currentTime; - -/** - @abstract The elapsed animation time since last callback. - */ -@property (readonly, nonatomic) CFTimeInterval elapsedTime; - -@end diff --git a/Pods/pop/pop/POPCustomAnimation.mm b/Pods/pop/pop/POPCustomAnimation.mm deleted file mode 100644 index 8cb7913..0000000 --- a/Pods/pop/pop/POPCustomAnimation.mm +++ /dev/null @@ -1,75 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPAnimationInternal.h" - -#import "POPCustomAnimation.h" - -@interface POPCustomAnimation () -@property (nonatomic, copy) POPCustomAnimationBlock animate; -@end - -@implementation POPCustomAnimation -@synthesize currentTime = _currentTime; -@synthesize elapsedTime = _elapsedTime; -@synthesize animate = _animate; - -+ (instancetype)animationWithBlock:(BOOL(^)(id target, POPCustomAnimation *))block -{ - POPCustomAnimation *b = [[self alloc] _init]; - b.animate = block; - return b; -} - -- (id)_init -{ - self = [super _init]; - if (nil != self) { - _state->type = kPOPAnimationCustom; - } - return self; -} - -- (CFTimeInterval)beginTime -{ - POPAnimationState *s = POPAnimationGetState(self); - return s->startTime > 0 ? s->startTime : s->beginTime; -} - -- (BOOL)_advance:(id)object currentTime:(CFTimeInterval)currentTime elapsedTime:(CFTimeInterval)elapsedTime -{ - _currentTime = currentTime; - _elapsedTime = elapsedTime; - return _animate(object, self); -} - -- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug -{ - [s appendFormat:@"; elapsedTime = %f; currentTime = %f;", _elapsedTime, _currentTime]; -} - -@end - -/** - * Note that only the animate block is copied, but not the current/elapsed times - */ -@implementation POPCustomAnimation (NSCopying) - -- (instancetype)copyWithZone:(NSZone *)zone { - - POPCustomAnimation *copy = [super copyWithZone:zone]; - - if (copy) { - copy.animate = self.animate; - } - - return copy; -} - -@end \ No newline at end of file diff --git a/Pods/pop/pop/POPDecayAnimation.h b/Pods/pop/pop/POPDecayAnimation.h deleted file mode 100644 index 92c6b60..0000000 --- a/Pods/pop/pop/POPDecayAnimation.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -/** - @abstract A concrete decay animation class. - @discussion Animation is achieved through gradual decay of animation value. - */ -@interface POPDecayAnimation : POPPropertyAnimation - -/** - @abstract The designated initializer. - @returns An instance of a decay animation. - */ -+ (instancetype)animation; - -/** - @abstract Convenience initializer that returns an animation with animatable property of name. - @param name The name of the animatable property. - @returns An instance of a decay animation configured with specified animatable property. - */ -+ (instancetype)animationWithPropertyNamed:(NSString *)name; - -/** - @abstract The current velocity value. - @discussion Set before animation start to account for initial velocity. Expressed in change of value units per second. The only POPValueTypes supported for velocity are: kPOPValuePoint, kPOPValueInteger, kPOPValueFloat, kPOPValueRect, and kPOPValueSize. - */ -@property (copy, nonatomic) id velocity; - -/** - @abstract The original velocity value. - @discussion Since the velocity property is modified as the animation progresses, this property stores the original, passed in velocity to support autoreverse and repeatCount. - */ -@property (copy, nonatomic, readonly) id originalVelocity; - -/** - @abstract The deceleration factor. - @discussion Values specifies should be in the range [0, 1]. Lower values results in faster deceleration. Defaults to 0.998. - */ -@property (assign, nonatomic) CGFloat deceleration; - -/** - @abstract The expected duration. - @discussion Derived based on input velocity and deceleration values. - */ -@property (readonly, assign, nonatomic) CFTimeInterval duration; - -/** - The to value is derived based on input velocity and deceleration. - */ -- (void)setToValue:(id)toValue NS_UNAVAILABLE; - -/** - @abstract The reversed velocity. - @discussion The reversed velocity based on the originalVelocity when the animation was set up. - */ -- (id)reversedVelocity; - -@end diff --git a/Pods/pop/pop/POPDecayAnimation.mm b/Pods/pop/pop/POPDecayAnimation.mm deleted file mode 100644 index 4698fd0..0000000 --- a/Pods/pop/pop/POPDecayAnimation.mm +++ /dev/null @@ -1,203 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPDecayAnimationInternal.h" - -#if TARGET_OS_IPHONE -#import -#endif - -const POPValueType supportedVelocityTypes[6] = { kPOPValuePoint, kPOPValueInteger, kPOPValueFloat, kPOPValueRect, kPOPValueSize, kPOPValueEdgeInsets }; - -@implementation POPDecayAnimation - -#pragma mark - Lifecycle - -#undef __state -#define __state ((POPDecayAnimationState *)_state) - -+ (instancetype)animation -{ - return [[self alloc] init]; -} - -+ (instancetype)animationWithPropertyNamed:(NSString *)aName -{ - POPDecayAnimation *anim = [self animation]; - anim.property = [POPAnimatableProperty propertyWithName:aName]; - return anim; -} - -- (id)init -{ - return [self _init]; -} - -- (void)_initState -{ - _state = new POPDecayAnimationState(self); -} - -#pragma mark - Properties - -DEFINE_RW_PROPERTY(POPDecayAnimationState, deceleration, setDeceleration:, CGFloat, __state->toVec = NULL;); - -@dynamic velocity; - -- (id)toValue -{ - [self _ensureComputedProperties]; - return POPBox(__state->toVec, __state->valueType); -} - -- (CFTimeInterval)duration -{ - [self _ensureComputedProperties]; - return __state->duration; -} - -- (void)setFromValue:(id)fromValue -{ - super.fromValue = fromValue; - [self _invalidateComputedProperties]; -} - -- (void)setToValue:(id)aValue -{ - // no-op - NSLog(@"ignoring to value on decay animation %@", self); -} - -- (id)reversedVelocity -{ - id reversedVelocity = nil; - - POPValueType velocityType = POPSelectValueType(self.originalVelocity, supportedVelocityTypes, POP_ARRAY_COUNT(supportedVelocityTypes)); - if (velocityType == kPOPValueFloat) { -#if CGFLOAT_IS_DOUBLE - CGFloat originalVelocityFloat = [(NSNumber *)self.originalVelocity doubleValue]; -#else - CGFloat originalVelocityFloat = [(NSNumber *)self.originalVelocity floatValue]; -#endif - NSNumber *negativeOriginalVelocityNumber = @(-originalVelocityFloat); - reversedVelocity = negativeOriginalVelocityNumber; - } else if (velocityType == kPOPValueInteger) { - NSInteger originalVelocityInteger = [(NSNumber *)self.originalVelocity integerValue]; - NSNumber *negativeOriginalVelocityNumber = @(-originalVelocityInteger); - reversedVelocity = negativeOriginalVelocityNumber; - } else if (velocityType == kPOPValuePoint) { - CGPoint originalVelocityPoint = [self.originalVelocity CGPointValue]; - CGPoint negativeOriginalVelocityPoint = CGPointMake(-originalVelocityPoint.x, -originalVelocityPoint.y); - reversedVelocity = [NSValue valueWithCGPoint:negativeOriginalVelocityPoint]; - } else if (velocityType == kPOPValueRect) { - CGRect originalVelocityRect = [self.originalVelocity CGRectValue]; - CGRect negativeOriginalVelocityRect = CGRectMake(-originalVelocityRect.origin.x, -originalVelocityRect.origin.y, -originalVelocityRect.size.width, -originalVelocityRect.size.height); - reversedVelocity = [NSValue valueWithCGRect:negativeOriginalVelocityRect]; - } else if (velocityType == kPOPValueSize) { - CGSize originalVelocitySize = [self.originalVelocity CGSizeValue]; - CGSize negativeOriginalVelocitySize = CGSizeMake(-originalVelocitySize.width, -originalVelocitySize.height); - reversedVelocity = [NSValue valueWithCGSize:negativeOriginalVelocitySize]; - } else if (velocityType == kPOPValueEdgeInsets) { -#if TARGET_OS_IPHONE - UIEdgeInsets originalVelocityInsets = [self.originalVelocity UIEdgeInsetsValue]; - UIEdgeInsets negativeOriginalVelocityInsets = UIEdgeInsetsMake(-originalVelocityInsets.top, -originalVelocityInsets.left, -originalVelocityInsets.bottom, -originalVelocityInsets.right); - reversedVelocity = [NSValue valueWithUIEdgeInsets:negativeOriginalVelocityInsets]; -#endif - } - - return reversedVelocity; -} - -- (id)originalVelocity -{ - return POPBox(__state->originalVelocityVec, __state->valueType); -} - -- (id)velocity -{ - return POPBox(__state->velocityVec, __state->valueType); -} - -- (void)setVelocity:(id)aValue -{ - POPValueType valueType = POPSelectValueType(aValue, supportedVelocityTypes, POP_ARRAY_COUNT(supportedVelocityTypes)); - if (valueType != kPOPValueUnknown) { - VectorRef vec = POPUnbox(aValue, __state->valueType, __state->valueCount, YES); - VectorRef origVec = POPUnbox(aValue, __state->valueType, __state->valueCount, YES); - - if (!vec_equal(vec, __state->velocityVec)) { - __state->velocityVec = vec; - __state->originalVelocityVec = origVec; - - if (__state->tracing) { - [__state->tracer updateVelocity:aValue]; - } - - [self _invalidateComputedProperties]; - - // automatically unpause active animations - if (__state->active && __state->paused) { - __state->fromVec = NULL; - __state->setPaused(false); - } - } - } else { - __state->velocityVec = NULL; - NSLog(@"Invalid velocity value for the decayAnimation: %@", aValue); - } -} - -#pragma mark - Utility - -- (void)_ensureComputedProperties -{ - if (NULL == __state->toVec) { - __state->computeDuration(); - __state->computeToValue(); - } -} - -- (void)_invalidateComputedProperties -{ - __state->toVec = NULL; - __state->duration = 0; -} - -- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug -{ - [super _appendDescription:s debug:debug]; - - if (0 != self.duration) { - [s appendFormat:@"; duration = %f", self.duration]; - } - - if (__state->deceleration) { - [s appendFormat:@"; deceleration = %f", __state->deceleration]; - } -} - -@end - -@implementation POPDecayAnimation (NSCopying) - -- (instancetype)copyWithZone:(NSZone *)zone { - - POPDecayAnimation *copy = [super copyWithZone:zone]; - - if (copy) { - // Set the velocity to the animation's original velocity, not its current. - copy.velocity = self.originalVelocity; - copy.deceleration = self.deceleration; - - } - - return copy; -} - -@end \ No newline at end of file diff --git a/Pods/pop/pop/POPDecayAnimationInternal.h b/Pods/pop/pop/POPDecayAnimationInternal.h deleted file mode 100644 index c101761..0000000 --- a/Pods/pop/pop/POPDecayAnimationInternal.h +++ /dev/null @@ -1,127 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPDecayAnimation.h" - -#import - -#import "POPPropertyAnimationInternal.h" - -// minimal velocity factor before decay animation is considered complete, in units / s -static CGFloat kPOPAnimationDecayMinimalVelocityFactor = 5.; - -// default decay animation deceleration -static CGFloat kPOPAnimationDecayDecelerationDefault = 0.998; - -static void decay_position(CGFloat *x, CGFloat *v, NSUInteger count, CFTimeInterval dt, CGFloat deceleration) -{ - dt *= 1000; - - // v0 = v / 1000 - // v = v0 * powf(deceleration, dt); - // v = v * 1000; - - // x0 = x; - // x = x0 + v0 * deceleration * (1 - powf(deceleration, dt)) / (1 - deceleration) - float v0[count]; - float kv = powf(deceleration, dt); - float kx = deceleration * (1 - kv) / (1 - deceleration); - - for (NSUInteger idx = 0; idx < count; idx++) { - v0[idx] = v[idx] / 1000.; - v[idx] = v0[idx] * kv * 1000.; - x[idx] = x[idx] + v0[idx] * kx; - } -} - -struct _POPDecayAnimationState : _POPPropertyAnimationState -{ - double deceleration; - CFTimeInterval duration; - - _POPDecayAnimationState(id __unsafe_unretained anim) : - _POPPropertyAnimationState(anim), - deceleration(kPOPAnimationDecayDecelerationDefault), - duration(0) - { - type = kPOPAnimationDecay; - } - - bool isDone() { - if (_POPPropertyAnimationState::isDone()) { - return true; - } - - CGFloat f = dynamicsThreshold * kPOPAnimationDecayMinimalVelocityFactor; - const CGFloat *velocityValues = vec_data(velocityVec); - for (NSUInteger idx = 0; idx < valueCount; idx++) { - if (std::abs((velocityValues[idx])) >= f) - return false; - } - return true; - - } - - void computeDuration() { - - // compute duration till threshold velocity - Vector4r scaledVelocity = vector4(velocityVec) / 1000.; - - double k = dynamicsThreshold * kPOPAnimationDecayMinimalVelocityFactor / 1000.; - double vx = k / scaledVelocity.x; - double vy = k / scaledVelocity.y; - double vz = k / scaledVelocity.z; - double vw = k / scaledVelocity.w; - double d = log(deceleration) * 1000.; - duration = MAX(MAX(MAX(log(fabs(vx)) / d, log(fabs(vy)) / d), log(fabs(vz)) / d), log(fabs(vw)) / d); - - // ensure velocity threshold is exceeded - if (std::isnan(duration) || duration < 0) { - duration = 0; - } - } - - void computeToValue() { - // to value assuming final velocity as a factor of dynamics threshold - // derived from v' = v * d^dt used in decay_position - // to compute the to value with maximal dt, p' = p + (v * d) / (1 - d) - VectorRef fromValue = NULL != currentVec ? currentVec : fromVec; - if (!fromValue) { - return; - } - - // ensure duration is computed - if (0 == duration) { - computeDuration(); - } - - // compute to value - VectorRef toValue(Vector::new_vector(fromValue.get())); - Vector4r velocity = velocityVec->vector4r(); - decay_position(toValue->data(), velocity.data(), valueCount, duration, deceleration); - toVec = toValue; - } - - bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) { - // advance past not yet initialized animations - if (NULL == currentVec) { - return false; - } - - decay_position(currentVec->data(), velocityVec->data(), valueCount, dt, deceleration); - - // clamp to compute end value; avoid possibility of decaying past - clampCurrentValue(kPOPAnimationClampEnd | clampMode); - - return true; - } - -}; - -typedef struct _POPDecayAnimationState POPDecayAnimationState; diff --git a/Pods/pop/pop/POPDefines.h b/Pods/pop/pop/POPDefines.h deleted file mode 100644 index eb28781..0000000 --- a/Pods/pop/pop/POPDefines.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#ifndef POP_POPDefines_h -#define POP_POPDefines_h - -#import - -#ifdef __cplusplus -# define POP_EXTERN_C_BEGIN extern "C" { -# define POP_EXTERN_C_END } -#else -# define POP_EXTERN_C_BEGIN -# define POP_EXTERN_C_END -#endif - -#define POP_ARRAY_COUNT(x) sizeof(x) / sizeof(x[0]) - -#if defined (__cplusplus) && defined (__GNUC__) -# define POP_NOTHROW __attribute__ ((nothrow)) -#else -# define POP_NOTHROW -#endif - -#if TARGET_OS_MAC - #define SCENEKIT_SDK_AVAILABLE defined(POP_USE_SCENEKIT) -#elif TARGET_OS_IPHONE - #define SCENEKIT_SDK_AVAILABLE defined(POP_USE_SCENEKIT) -#endif - -#endif diff --git a/Pods/pop/pop/POPGeometry.h b/Pods/pop/pop/POPGeometry.h deleted file mode 100644 index 8ba07e3..0000000 --- a/Pods/pop/pop/POPGeometry.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#if TARGET_OS_IPHONE -#import -#endif - -#if !TARGET_OS_IPHONE - -/** NSValue extensions to support animatable types. */ -@interface NSValue (POP) - -/** - @abstract Creates an NSValue given a CGPoint. - */ -+ (NSValue *)valueWithCGPoint:(CGPoint)point; - -/** - @abstract Creates an NSValue given a CGSize. - */ -+ (NSValue *)valueWithCGSize:(CGSize)size; - -/** - @abstract Creates an NSValue given a CGRect. - */ -+ (NSValue *)valueWithCGRect:(CGRect)rect; - -/** - @abstract Creates an NSValue given a CFRange. - */ -+ (NSValue *)valueWithCFRange:(CFRange)range; - -/** - @abstract Creates an NSValue given a CGAffineTransform. - */ -+ (NSValue *)valueWithCGAffineTransform:(CGAffineTransform)transform; - -/** - @abstract Returns the underlying CGPoint value. - */ -- (CGPoint)CGPointValue; - -/** - @abstract Returns the underlying CGSize value. - */ -- (CGSize)CGSizeValue; - -/** - @abstract Returns the underlying CGRect value. - */ -- (CGRect)CGRectValue; - -/** - @abstract Returns the underlying CFRange value. - */ -- (CFRange)CFRangeValue; - -/** - @abstract Returns the underlying CGAffineTransform value. - */ -- (CGAffineTransform)CGAffineTransformValue; - -@end - -#endif diff --git a/Pods/pop/pop/POPGeometry.mm b/Pods/pop/pop/POPGeometry.mm deleted file mode 100644 index 41998b1..0000000 --- a/Pods/pop/pop/POPGeometry.mm +++ /dev/null @@ -1,94 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPGeometry.h" - -#if !TARGET_OS_IPHONE -@implementation NSValue (POP) - -+ (NSValue *)valueWithCGPoint:(CGPoint)point { - return [NSValue valueWithBytes:&point objCType:@encode(CGPoint)]; -} - -+ (NSValue *)valueWithCGSize:(CGSize)size { - return [NSValue valueWithBytes:&size objCType:@encode(CGSize)]; -} - -+ (NSValue *)valueWithCGRect:(CGRect)rect { - return [NSValue valueWithBytes:&rect objCType:@encode(CGRect)]; -} - -+ (NSValue *)valueWithCFRange:(CFRange)range { - return [NSValue valueWithBytes:&range objCType:@encode(CFRange)]; -} - -+ (NSValue *)valueWithCGAffineTransform:(CGAffineTransform)transform -{ - return [NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)]; -} - -- (CGPoint)CGPointValue { - CGPoint result; - [self getValue:&result]; - return result; -} - -- (CGSize)CGSizeValue { - CGSize result; - [self getValue:&result]; - return result; -} - -- (CGRect)CGRectValue { - CGRect result; - [self getValue:&result]; - return result; -} - -- (CFRange)CFRangeValue { - CFRange result; - [self getValue:&result]; - return result; -} - -- (CGAffineTransform)CGAffineTransformValue { - CGAffineTransform result; - [self getValue:&result]; - return result; -} -@end - -#endif - -#if TARGET_OS_IPHONE -#import "POPDefines.h" - -#if SCENEKIT_SDK_AVAILABLE -#import - -/** - Dirty hacks because iOS is weird and decided to define both SCNVector3's and SCNVector4's objCType as "t". However @encode(SCNVector3) and @encode(SCNVector4) both return the proper definition ("{SCNVector3=fff}" and "{SCNVector4=ffff}" respectively) - - [[NSValue valueWithSCNVector3:SCNVector3Make(0.0, 0.0, 0.0)] objcType] returns "t", whereas it should return "{SCNVector3=fff}". - - *flips table* - */ -@implementation NSValue (SceneKitFixes) - -+ (NSValue *)valueWithSCNVector3:(SCNVector3)vec3 { - return [NSValue valueWithBytes:&vec3 objCType:@encode(SCNVector3)]; -} - -+ (NSValue *)valueWithSCNVector4:(SCNVector4)vec4 { - return [NSValue valueWithBytes:&vec4 objCType:@encode(SCNVector4)]; -} - -@end -#endif -#endif diff --git a/Pods/pop/pop/POPLayerExtras.h b/Pods/pop/pop/POPLayerExtras.h deleted file mode 100644 index ec4c29a..0000000 --- a/Pods/pop/pop/POPLayerExtras.h +++ /dev/null @@ -1,196 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import - -POP_EXTERN_C_BEGIN - -#pragma mark - Scale - -/** - @abstract Returns layer scale factor for the x axis. - */ -extern CGFloat POPLayerGetScaleX(CALayer *l); - -/** - @abstract Set layer scale factor for the x axis. - */ -extern void POPLayerSetScaleX(CALayer *l, CGFloat f); - -/** - @abstract Returns layer scale factor for the y axis. - */ -extern CGFloat POPLayerGetScaleY(CALayer *l); - -/** - @abstract Set layer scale factor for the y axis. - */ -extern void POPLayerSetScaleY(CALayer *l, CGFloat f); - -/** - @abstract Returns layer scale factor for the z axis. - */ -extern CGFloat POPLayerGetScaleZ(CALayer *l); - -/** - @abstract Set layer scale factor for the z axis. - */ -extern void POPLayerSetScaleZ(CALayer *l, CGFloat f); - -/** - @abstract Returns layer scale factors for x and y access as point. - */ -extern CGPoint POPLayerGetScaleXY(CALayer *l); - -/** - @abstract Sets layer x and y scale factors given point. - */ -extern void POPLayerSetScaleXY(CALayer *l, CGPoint p); - -#pragma mark - Translation - -/** - @abstract Returns layer translation factor for the x axis. - */ -extern CGFloat POPLayerGetTranslationX(CALayer *l); - -/** - @abstract Set layer translation factor for the x axis. - */ -extern void POPLayerSetTranslationX(CALayer *l, CGFloat f); - -/** - @abstract Returns layer translation factor for the y axis. - */ -extern CGFloat POPLayerGetTranslationY(CALayer *l); - -/** - @abstract Set layer translation factor for the y axis. - */ -extern void POPLayerSetTranslationY(CALayer *l, CGFloat f); - -/** - @abstract Returns layer translation factor for the z axis. - */ -extern CGFloat POPLayerGetTranslationZ(CALayer *l); - -/** - @abstract Set layer translation factor for the z axis. - */ -extern void POPLayerSetTranslationZ(CALayer *l, CGFloat f); - -/** - @abstract Returns layer translation factors for x and y access as point. - */ -extern CGPoint POPLayerGetTranslationXY(CALayer *l); - -/** - @abstract Sets layer x and y translation factors given point. - */ -extern void POPLayerSetTranslationXY(CALayer *l, CGPoint p); - -#pragma mark - Rotation - -/** - @abstract Returns layer rotation, in radians, in the X axis. - */ -extern CGFloat POPLayerGetRotationX(CALayer *l); - -/** - @abstract Sets layer rotation, in radians, in the X axis. - */ -extern void POPLayerSetRotationX(CALayer *l, CGFloat f); - -/** - @abstract Returns layer rotation, in radians, in the Y axis. - */ -extern CGFloat POPLayerGetRotationY(CALayer *l); - -/** - @abstract Sets layer rotation, in radians, in the Y axis. - */ -extern void POPLayerSetRotationY(CALayer *l, CGFloat f); - -/** - @abstract Returns layer rotation, in radians, in the Z axis. - */ -extern CGFloat POPLayerGetRotationZ(CALayer *l); - -/** - @abstract Sets layer rotation, in radians, in the Z axis. - */ -extern void POPLayerSetRotationZ(CALayer *l, CGFloat f); - -/** - @abstract Returns layer rotation, in radians, in the Z axis. - */ -extern CGFloat POPLayerGetRotation(CALayer *l); - -/** - @abstract Sets layer rotation, in radians, in the Z axis. - */ -extern void POPLayerSetRotation(CALayer *l, CGFloat f); - -#pragma mark - Sublayer Scale - -/** - @abstract Returns sublayer scale factors for x and y access as point. - */ -extern CGPoint POPLayerGetSubScaleXY(CALayer *l); - -/** - @abstract Sets sublayer x and y scale factors given point. - */ -extern void POPLayerSetSubScaleXY(CALayer *l, CGPoint p); - -#pragma mark - Sublayer Translation - -/** - @abstract Returns sublayer translation factor for the x axis. - */ -extern CGFloat POPLayerGetSubTranslationX(CALayer *l); - -/** - @abstract Set sublayer translation factor for the x axis. - */ -extern void POPLayerSetSubTranslationX(CALayer *l, CGFloat f); - -/** - @abstract Returns sublayer translation factor for the y axis. - */ -extern CGFloat POPLayerGetSubTranslationY(CALayer *l); - -/** - @abstract Set sublayer translation factor for the y axis. - */ -extern void POPLayerSetSubTranslationY(CALayer *l, CGFloat f); - -/** - @abstract Returns sublayer translation factor for the z axis. - */ -extern CGFloat POPLayerGetSubTranslationZ(CALayer *l); - -/** - @abstract Set sublayer translation factor for the z axis. - */ -extern void POPLayerSetSubTranslationZ(CALayer *l, CGFloat f); - -/** - @abstract Returns sublayer translation factors for x and y access as point. - */ -extern CGPoint POPLayerGetSubTranslationXY(CALayer *l); - -/** - @abstract Sets sublayer x and y translation factors given point. - */ -extern void POPLayerSetSubTranslationXY(CALayer *l, CGPoint p); - -POP_EXTERN_C_END diff --git a/Pods/pop/pop/POPLayerExtras.mm b/Pods/pop/pop/POPLayerExtras.mm deleted file mode 100644 index c8ad7f9..0000000 --- a/Pods/pop/pop/POPLayerExtras.mm +++ /dev/null @@ -1,288 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPLayerExtras.h" - -#include "TransformationMatrix.h" - -using namespace WebCore; - -#define DECOMPOSE_TRANSFORM(L) \ - TransformationMatrix _m(L.transform); \ - TransformationMatrix::DecomposedType _d; \ - _m.decompose(_d); - -#define RECOMPOSE_TRANSFORM(L) \ - _m.recompose(_d); \ - L.transform = _m.transform3d(); - -#define RECOMPOSE_ROT_TRANSFORM(L) \ - _m.recompose(_d, true); \ - L.transform = _m.transform3d(); - -#define DECOMPOSE_SUBLAYER_TRANSFORM(L) \ - TransformationMatrix _m(L.sublayerTransform); \ - TransformationMatrix::DecomposedType _d; \ - _m.decompose(_d); - -#define RECOMPOSE_SUBLAYER_TRANSFORM(L) \ - _m.recompose(_d); \ - L.sublayerTransform = _m.transform3d(); - -#pragma mark - Scale - -NS_INLINE void ensureNonZeroValue(CGFloat &f) -{ - if (f == 0) { - f = 1e-6; - } -} - -NS_INLINE void ensureNonZeroValue(CGPoint &p) -{ - if (p.x == 0 && p.y == 0) { - p.x = 1e-6; - p.y = 1e-6; - } -} - -CGFloat POPLayerGetScaleX(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return _d.scaleX; -} - -void POPLayerSetScaleX(CALayer *l, CGFloat f) -{ - ensureNonZeroValue(f); - DECOMPOSE_TRANSFORM(l); - _d.scaleX = f; - RECOMPOSE_TRANSFORM(l); -} - -CGFloat POPLayerGetScaleY(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return _d.scaleY; -} - -void POPLayerSetScaleY(CALayer *l, CGFloat f) -{ - ensureNonZeroValue(f); - DECOMPOSE_TRANSFORM(l); - _d.scaleY = f; - RECOMPOSE_TRANSFORM(l); -} - -CGFloat POPLayerGetScaleZ(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return _d.scaleZ; -} - -void POPLayerSetScaleZ(CALayer *l, CGFloat f) -{ - ensureNonZeroValue(f); - DECOMPOSE_TRANSFORM(l); - _d.scaleZ = f; - RECOMPOSE_TRANSFORM(l); -} - -CGPoint POPLayerGetScaleXY(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return CGPointMake(_d.scaleX, _d.scaleY); -} - -void POPLayerSetScaleXY(CALayer *l, CGPoint p) -{ - ensureNonZeroValue(p); - DECOMPOSE_TRANSFORM(l); - _d.scaleX = p.x; - _d.scaleY = p.y; - RECOMPOSE_TRANSFORM(l); -} - -#pragma mark - Translation - -CGFloat POPLayerGetTranslationX(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return _d.translateX; -} - -void POPLayerSetTranslationX(CALayer *l, CGFloat f) -{ - DECOMPOSE_TRANSFORM(l); - _d.translateX = f; - RECOMPOSE_TRANSFORM(l); -} - -CGFloat POPLayerGetTranslationY(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return _d.translateY; -} - -void POPLayerSetTranslationY(CALayer *l, CGFloat f) -{ - DECOMPOSE_TRANSFORM(l); - _d.translateY = f; - RECOMPOSE_TRANSFORM(l); -} - -CGFloat POPLayerGetTranslationZ(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return _d.translateZ; -} - -void POPLayerSetTranslationZ(CALayer *l, CGFloat f) -{ - DECOMPOSE_TRANSFORM(l); - _d.translateZ = f; - RECOMPOSE_TRANSFORM(l); -} - -CGPoint POPLayerGetTranslationXY(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return CGPointMake(_d.translateX, _d.translateY); -} - -void POPLayerSetTranslationXY(CALayer *l, CGPoint p) -{ - DECOMPOSE_TRANSFORM(l); - _d.translateX = p.x; - _d.translateY = p.y; - RECOMPOSE_TRANSFORM(l); -} - -#pragma mark - Rotation - -CGFloat POPLayerGetRotationX(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return _d.rotateX; -} - -void POPLayerSetRotationX(CALayer *l, CGFloat f) -{ - DECOMPOSE_TRANSFORM(l); - _d.rotateX = f; - RECOMPOSE_ROT_TRANSFORM(l); -} - -CGFloat POPLayerGetRotationY(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return _d.rotateY; -} - -void POPLayerSetRotationY(CALayer *l, CGFloat f) -{ - DECOMPOSE_TRANSFORM(l); - _d.rotateY = f; - RECOMPOSE_ROT_TRANSFORM(l); -} - -CGFloat POPLayerGetRotationZ(CALayer *l) -{ - DECOMPOSE_TRANSFORM(l); - return _d.rotateZ; -} - -void POPLayerSetRotationZ(CALayer *l, CGFloat f) -{ - DECOMPOSE_TRANSFORM(l); - _d.rotateZ = f; - RECOMPOSE_ROT_TRANSFORM(l); -} - -CGFloat POPLayerGetRotation(CALayer *l) -{ - return POPLayerGetRotationZ(l); -} - -void POPLayerSetRotation(CALayer *l, CGFloat f) -{ - POPLayerSetRotationZ(l, f); -} - -#pragma mark - Sublayer Scale - -CGPoint POPLayerGetSubScaleXY(CALayer *l) -{ - DECOMPOSE_SUBLAYER_TRANSFORM(l); - return CGPointMake(_d.scaleX, _d.scaleY); -} - -void POPLayerSetSubScaleXY(CALayer *l, CGPoint p) -{ - ensureNonZeroValue(p); - DECOMPOSE_SUBLAYER_TRANSFORM(l); - _d.scaleX = p.x; - _d.scaleY = p.y; - RECOMPOSE_SUBLAYER_TRANSFORM(l); -} - -#pragma mark - Sublayer Translation - -extern CGFloat POPLayerGetSubTranslationX(CALayer *l) -{ - DECOMPOSE_SUBLAYER_TRANSFORM(l); - return _d.translateX; -} - -extern void POPLayerSetSubTranslationX(CALayer *l, CGFloat f) -{ - DECOMPOSE_SUBLAYER_TRANSFORM(l); - _d.translateX = f; - RECOMPOSE_SUBLAYER_TRANSFORM(l); -} - -extern CGFloat POPLayerGetSubTranslationY(CALayer *l) -{ - DECOMPOSE_SUBLAYER_TRANSFORM(l); - return _d.translateY; -} - -extern void POPLayerSetSubTranslationY(CALayer *l, CGFloat f) -{ - DECOMPOSE_SUBLAYER_TRANSFORM(l); - _d.translateY = f; - RECOMPOSE_SUBLAYER_TRANSFORM(l); -} - -extern CGFloat POPLayerGetSubTranslationZ(CALayer *l) -{ - DECOMPOSE_SUBLAYER_TRANSFORM(l); - return _d.translateZ; -} - -extern void POPLayerSetSubTranslationZ(CALayer *l, CGFloat f) -{ - DECOMPOSE_SUBLAYER_TRANSFORM(l); - _d.translateZ = f; - RECOMPOSE_SUBLAYER_TRANSFORM(l); -} - -extern CGPoint POPLayerGetSubTranslationXY(CALayer *l) -{ - DECOMPOSE_SUBLAYER_TRANSFORM(l); - return CGPointMake(_d.translateX, _d.translateY); -} - -extern void POPLayerSetSubTranslationXY(CALayer *l, CGPoint p) -{ - DECOMPOSE_SUBLAYER_TRANSFORM(l); - _d.translateX = p.x; - _d.translateY = p.y; - RECOMPOSE_SUBLAYER_TRANSFORM(l); -} diff --git a/Pods/pop/pop/POPMath.h b/Pods/pop/pop/POPMath.h deleted file mode 100644 index 0c6f5e2..0000000 --- a/Pods/pop/pop/POPMath.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import - -#import "POPDefines.h" -#import "POPVector.h" - -NS_INLINE CGFloat sqrtr(CGFloat f) -{ -#if CGFLOAT_IS_DOUBLE - return sqrt(f); -#else - return sqrtf(f); -#endif -} - -// round to nearest sub; pass 2.0 to round to every 0.5 (eg: retina pixels) -NS_INLINE CGFloat POPSubRound(CGFloat f, CGFloat sub) -{ - return round(f * sub) / sub; -} - -#define MIX(a, b, f) ((a) + (f) * ((b) - (a))) - -// the longer the duration, the higher the necessary precision -#define SOLVE_EPS(dur) (1. / (1000. * (dur))) - -#define _EQLF_(x, y, epsilon) (fabsf ((x) - (y)) < epsilon) - -extern void POPInterpolateVector(NSUInteger count, CGFloat *dst, const CGFloat *from, const CGFloat *to, CGFloat f); - -extern double POPTimingFunctionSolve(const double vec[4], double t, double eps); - -// quadratic mapping of t [0, 1] to [start, end] -extern double POPQuadraticOutInterpolation(double t, double start, double end); - -// normalize value to [0, 1] based on its range [startValue, endValue] -extern double POPNormalize(double value, double startValue, double endValue); - -// project a normalized value [0, 1] to a given range [start, end] -extern double POPProjectNormal(double n, double start, double end); - -// solve a quadratic equation of the form a * x^2 + b * x + c = 0 -extern void POPQuadraticSolve(CGFloat a, CGFloat b, CGFloat c, CGFloat &x1, CGFloat &x2); - -// for a given tension return the bouncy 3 friction that produces no bounce -extern double POPBouncy3NoBounce(double tension); diff --git a/Pods/pop/pop/POPMath.mm b/Pods/pop/pop/POPMath.mm deleted file mode 100644 index 69a506a..0000000 --- a/Pods/pop/pop/POPMath.mm +++ /dev/null @@ -1,83 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPMath.h" - -#import "POPAnimationPrivate.h" -#import "UnitBezier.h" - -void POPInterpolateVector(NSUInteger count, CGFloat *dst, const CGFloat *from, const CGFloat *to, CGFloat f) -{ - for (NSUInteger idx = 0; idx < count; idx++) { - dst[idx] = MIX(from[idx], to[idx], f); - } -} - -double POPTimingFunctionSolve(const double vec[4], double t, double eps) -{ - WebCore::UnitBezier bezier(vec[0], vec[1], vec[2], vec[3]); - return bezier.solve(t, eps); -} - -double POPNormalize(double value, double startValue, double endValue) -{ - return (value - startValue) / (endValue - startValue); -} - -double POPProjectNormal(double n, double start, double end) -{ - return start + (n * (end - start)); -} - -static double linear_interpolation(double t, double start, double end) -{ - return t * end + (1.f - t) * start; -} - -double POPQuadraticOutInterpolation(double t, double start, double end) -{ - return linear_interpolation(2*t - t*t, start, end); -} - -static double b3_friction1(double x) -{ - return (0.0007 * pow(x, 3)) - (0.031 * pow(x, 2)) + 0.64 * x + 1.28; -} - -static double b3_friction2(double x) -{ - return (0.000044 * pow(x, 3)) - (0.006 * pow(x, 2)) + 0.36 * x + 2.; -} - -static double b3_friction3(double x) -{ - return (0.00000045 * pow(x, 3)) - (0.000332 * pow(x, 2)) + 0.1078 * x + 5.84; -} - -double POPBouncy3NoBounce(double tension) -{ - double friction = 0; - if (tension <= 18.) { - friction = b3_friction1(tension); - } else if (tension > 18 && tension <= 44) { - friction = b3_friction2(tension); - } else if (tension > 44) { - friction = b3_friction3(tension); - } else { - assert(false); - } - return friction; -} - -void POPQuadraticSolve(CGFloat a, CGFloat b, CGFloat c, CGFloat &x1, CGFloat &x2) -{ - CGFloat discriminant = sqrt(b * b - 4 * a * c); - x1 = (-b + discriminant) / (2 * a); - x2 = (-b - discriminant) / (2 * a); -} diff --git a/Pods/pop/pop/POPPropertyAnimation.h b/Pods/pop/pop/POPPropertyAnimation.h deleted file mode 100644 index 2861665..0000000 --- a/Pods/pop/pop/POPPropertyAnimation.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import -#import - -/** - @abstract Flags for clamping animation values. - @discussion Animation values can optionally be clamped to avoid overshoot. kPOPAnimationClampStart ensures values are more than fromValue and kPOPAnimationClampEnd ensures values are less than toValue. - */ -typedef NS_OPTIONS(NSUInteger, POPAnimationClampFlags) -{ - kPOPAnimationClampNone = 0, - kPOPAnimationClampStart = 1UL << 0, - kPOPAnimationClampEnd = 1UL << 1, - kPOPAnimationClampBoth = kPOPAnimationClampStart | kPOPAnimationClampEnd, -}; - -/** - @abstract The semi-concrete property animation subclass. - */ -@interface POPPropertyAnimation : POPAnimation - -/** - @abstract The property to animate. - */ -@property (strong, nonatomic) POPAnimatableProperty *property; - -/** - @abstract The value to animate from. - @discussion The value type should match the property. If unspecified, the value is initialized to the object's current value on animation start. - */ -@property (copy, nonatomic) id fromValue; - -/** - @abstract The value to animate to. - @discussion The value type should match the property. If unspecified, the value is initialized to the object's current value on animation start. - */ -@property (copy, nonatomic) id toValue; - -/** - @abstract The rounding factor applied to the current animated value. - @discussion Specify 1.0 to animate between integral values. Defaults to 0 meaning no rounding. - */ -@property (assign, nonatomic) CGFloat roundingFactor; - -/** - @abstract The clamp mode applied to the current animated value. - @discussion See {@ref POPAnimationClampFlags} for possible values. Defaults to kPOPAnimationClampNone. - */ -@property (assign, nonatomic) NSUInteger clampMode; - -/** - @abstract The flag indicating whether values should be "added" each frame, rather than set. - @discussion Addition may be type dependent. Defaults to NO. - */ -@property (assign, nonatomic, getter = isAdditive) BOOL additive; - -@end diff --git a/Pods/pop/pop/POPPropertyAnimation.mm b/Pods/pop/pop/POPPropertyAnimation.mm deleted file mode 100644 index 56a9e5c..0000000 --- a/Pods/pop/pop/POPPropertyAnimation.mm +++ /dev/null @@ -1,125 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPPropertyAnimationInternal.h" - -@implementation POPPropertyAnimation - -#pragma mark - Lifecycle - -#undef __state -#define __state ((POPPropertyAnimationState *)_state) - -- (void)_initState -{ - _state = new POPPropertyAnimationState(self); -} - -#pragma mark - Properties - -DEFINE_RW_FLAG(POPPropertyAnimationState, additive, isAdditive, setAdditive:); -DEFINE_RW_PROPERTY(POPPropertyAnimationState, roundingFactor, setRoundingFactor:, CGFloat); -DEFINE_RW_PROPERTY(POPPropertyAnimationState, clampMode, setClampMode:, NSUInteger); -DEFINE_RW_PROPERTY_OBJ(POPPropertyAnimationState, property, setProperty:, POPAnimatableProperty*, ((POPPropertyAnimationState*)_state)->updatedDynamicsThreshold();); -DEFINE_RW_PROPERTY_OBJ_COPY(POPPropertyAnimationState, progressMarkers, setProgressMarkers:, NSArray*, ((POPPropertyAnimationState*)_state)->updatedProgressMarkers();); - -- (id)fromValue -{ - return POPBox(__state->fromVec, __state->valueType); -} - -- (void)setFromValue:(id)aValue -{ - POPPropertyAnimationState *s = __state; - VectorRef vec = POPUnbox(aValue, s->valueType, s->valueCount, YES); - if (!vec_equal(vec, s->fromVec)) { - s->fromVec = vec; - - if (s->tracing) { - [s->tracer updateFromValue:aValue]; - } - } -} - -- (id)toValue -{ - return POPBox(__state->toVec, __state->valueType); -} - -- (void)setToValue:(id)aValue -{ - POPPropertyAnimationState *s = __state; - VectorRef vec = POPUnbox(aValue, s->valueType, s->valueCount, YES); - - if (!vec_equal(vec, s->toVec)) { - s->toVec = vec; - - // invalidate to dependent state - s->didReachToValue = false; - s->distanceVec = NULL; - - if (s->tracing) { - [s->tracer updateToValue:aValue]; - } - - // automatically unpause active animations - if (s->active && s->paused) { - s->setPaused(false); - } - } -} - -- (id)currentValue -{ - return POPBox(__state->currentValue(), __state->valueType); -} - -#pragma mark - Utility - -- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug -{ - [s appendFormat:@"; from = %@; to = %@", describe(__state->fromVec), describe(__state->toVec)]; - - if (_state->active) - [s appendFormat:@"; currentValue = %@", describe(__state->currentValue())]; - - if (__state->velocityVec && 0 != __state->velocityVec->norm()) - [s appendFormat:@"; velocity = %@", describe(__state->velocityVec)]; - - if (!self.removedOnCompletion) - [s appendFormat:@"; removedOnCompletion = %@", POPStringFromBOOL(self.removedOnCompletion)]; - - if (__state->progressMarkers) - [s appendFormat:@"; progressMarkers = [%@]", [__state->progressMarkers componentsJoinedByString:@", "]]; - - if (_state->active) - [s appendFormat:@"; progress = %f", __state->progress]; -} - -@end - -@implementation POPPropertyAnimation (NSCopying) - -- (instancetype)copyWithZone:(NSZone *)zone { - - POPPropertyAnimation *copy = [super copyWithZone:zone]; - - if (copy) { - copy.property = [self.property copyWithZone:zone]; - copy.fromValue = self.fromValue; - copy.toValue = self.toValue; - copy.roundingFactor = self.roundingFactor; - copy.clampMode = self.clampMode; - copy.additive = self.additive; - } - - return copy; -} - -@end \ No newline at end of file diff --git a/Pods/pop/pop/POPPropertyAnimationInternal.h b/Pods/pop/pop/POPPropertyAnimationInternal.h deleted file mode 100644 index 9f2aee4..0000000 --- a/Pods/pop/pop/POPPropertyAnimationInternal.h +++ /dev/null @@ -1,359 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPAnimationInternal.h" -#import "POPPropertyAnimation.h" - -static void clampValue(CGFloat &value, CGFloat fromValue, CGFloat toValue, NSUInteger clamp) -{ - BOOL increasing = (toValue > fromValue); - - // Clamp start of animation. - if ((kPOPAnimationClampStart & clamp) && - ((increasing && (value < fromValue)) || (!increasing && (value > fromValue)))) { - value = fromValue; - } - - // Clamp end of animation. - if ((kPOPAnimationClampEnd & clamp) && - ((increasing && (value > toValue)) || (!increasing && (value < toValue)))) { - value = toValue; - } -} - -struct _POPPropertyAnimationState : _POPAnimationState -{ - POPAnimatableProperty *property; - POPValueType valueType; - NSUInteger valueCount; - VectorRef fromVec; - VectorRef toVec; - VectorRef currentVec; - VectorRef previousVec; - VectorRef previous2Vec; - VectorRef velocityVec; - VectorRef originalVelocityVec; - VectorRef distanceVec; - CGFloat roundingFactor; - NSUInteger clampMode; - NSArray *progressMarkers; - POPProgressMarker *progressMarkerState; - NSUInteger progressMarkerCount; - NSUInteger nextProgressMarkerIdx; - CGFloat dynamicsThreshold; - - _POPPropertyAnimationState(id __unsafe_unretained anim) : _POPAnimationState(anim), - property(nil), - valueType((POPValueType)0), - valueCount(0), - fromVec(nullptr), - toVec(nullptr), - currentVec(nullptr), - previousVec(nullptr), - previous2Vec(nullptr), - velocityVec(nullptr), - originalVelocityVec(nullptr), - distanceVec(nullptr), - roundingFactor(0), - clampMode(0), - progressMarkers(nil), - progressMarkerState(nil), - progressMarkerCount(0), - nextProgressMarkerIdx(0), - dynamicsThreshold(0) - { - type = kPOPAnimationBasic; - } - - ~_POPPropertyAnimationState() - { - if (progressMarkerState) { - free(progressMarkerState); - progressMarkerState = NULL; - } - } - - bool canProgress() { - return hasValue(); - } - - bool shouldRound() { - return 0 != roundingFactor; - } - - bool hasValue() { - return 0 != valueCount; - } - - bool isDone() { - // inherit done - if (_POPAnimationState::isDone()) { - return true; - } - - // consider an animation with no values done - if (!hasValue() && !isCustom()) { - return true; - } - - return false; - } - - // returns a copy of the currentVec, rounding if needed - VectorRef currentValue() { - VectorRef vec = VectorRef(Vector::new_vector(currentVec.get())); - if (shouldRound()) { - vec->subRound(1 / roundingFactor); - } - return vec; - } - - void resetProgressMarkerState() - { - for (NSUInteger idx = 0; idx < progressMarkerCount; idx++) - progressMarkerState[idx].reached = false; - - nextProgressMarkerIdx = 0; - } - - void updatedProgressMarkers() - { - if (progressMarkerState) { - free(progressMarkerState); - progressMarkerState = NULL; - } - - progressMarkerCount = progressMarkers.count; - - if (0 != progressMarkerCount) { - progressMarkerState = (POPProgressMarker *)malloc(progressMarkerCount * sizeof(POPProgressMarker)); - [progressMarkers enumerateObjectsUsingBlock:^(NSNumber *progressMarker, NSUInteger idx, BOOL *stop) { - progressMarkerState[idx].reached = false; - progressMarkerState[idx].progress = [progressMarker floatValue]; - }]; - } - - nextProgressMarkerIdx = 0; - } - - virtual void updatedDynamicsThreshold() - { - dynamicsThreshold = property.threshold; - } - - void finalizeProgress() - { - progress = 1.0; - NSUInteger count = valueCount; - VectorRef outVec(Vector::new_vector(count, NULL)); - - if (outVec && toVec) { - *outVec = *toVec; - } - - currentVec = outVec; - clampCurrentValue(); - delegateProgress(); - } - - void computeProgress() { - if (!canProgress()) { - return; - } - - static ComputeProgressFunctor func; - Vector4r v = vector4(currentVec); - Vector4r f = vector4(fromVec); - Vector4r t = vector4(toVec); - progress = func(v, f, t); - } - - void delegateProgress() { - if (!canProgress()) { - return; - } - - if (delegateDidProgress && progressMarkerState) { - - while (nextProgressMarkerIdx < progressMarkerCount) { - if (progress < progressMarkerState[nextProgressMarkerIdx].progress) - break; - - if (!progressMarkerState[nextProgressMarkerIdx].reached) { - ActionEnabler enabler; - [delegate pop_animation:self didReachProgress:progressMarkerState[nextProgressMarkerIdx].progress]; - progressMarkerState[nextProgressMarkerIdx].reached = true; - } - - nextProgressMarkerIdx++; - } - } - - if (!didReachToValue) { - bool didReachToValue = false; - if (0 == valueCount) { - didReachToValue = true; - } else { - Vector4r distance = toVec->vector4r(); - distance -= currentVec->vector4r(); - - if (0 == distance.squaredNorm()) { - didReachToValue = true; - } else { - // components - if (distanceVec) { - didReachToValue = true; - const CGFloat *distanceValues = distanceVec->data(); - for (NSUInteger idx = 0; idx < valueCount; idx++) { - didReachToValue &= (signbit(distance[idx]) != signbit(distanceValues[idx])); - } - } - } - } - - if (didReachToValue) { - handleDidReachToValue(); - } - } - } - - void handleDidReachToValue() { - didReachToValue = true; - - if (delegateDidReachToValue) { - ActionEnabler enabler; - [delegate pop_animationDidReachToValue:self]; - } - - POPAnimationDidReachToValueBlock block = animationDidReachToValueBlock; - if (block != NULL) { - ActionEnabler enabler; - block(self); - } - - if (tracing) { - [tracer didReachToValue:POPBox(currentValue(), valueType, true)]; - } - } - - void readObjectValue(VectorRef *ptrVec, id obj) - { - // use current object value as from value - pop_animatable_read_block read = property.readBlock; - if (NULL != read) { - - Vector4r vec = read_values(read, obj, valueCount); - *ptrVec = VectorRef(Vector::new_vector(valueCount, vec)); - - if (tracing) { - [tracer readPropertyValue:POPBox(*ptrVec, valueType, true)]; - } - } - } - - virtual void willRun(bool started, id obj) { - // ensure from value initialized - if (NULL == fromVec) { - readObjectValue(&fromVec, obj); - } - - // ensure to value initialized - if (NULL == toVec) { - // compute decay to value - if (kPOPAnimationDecay == type) { - [self toValue]; - } else { - // read to value - readObjectValue(&toVec, obj); - } - } - - // handle one time value initialization on start - if (started) { - - // initialize current vec - if (!currentVec) { - currentVec = VectorRef(Vector::new_vector(valueCount, NULL)); - - // initialize current value with from value - // only do this on initial creation to avoid overwriting current value - // on paused animation continuation - if (currentVec && fromVec) { - *currentVec = *fromVec; - } - } - - // ensure velocity values - if (!velocityVec) { - velocityVec = VectorRef(Vector::new_vector(valueCount, NULL)); - } - if (!originalVelocityVec) { - originalVelocityVec = VectorRef(Vector::new_vector(valueCount, NULL)); - } - } - - // ensure distance value initialized - // depends on current value set on one time start - if (NULL == distanceVec) { - - // not yet started animations may not have current value - VectorRef fromVec2 = NULL != currentVec ? currentVec : fromVec; - - if (fromVec2 && toVec) { - Vector4r distance = toVec->vector4r(); - distance -= fromVec2->vector4r(); - - if (0 != distance.squaredNorm()) { - distanceVec = VectorRef(Vector::new_vector(valueCount, distance)); - } - } - } - } - - virtual void reset(bool all) { - _POPAnimationState::reset(all); - - if (all) { - currentVec = NULL; - previousVec = NULL; - previous2Vec = NULL; - } - progress = 0; - resetProgressMarkerState(); - didReachToValue = false; - distanceVec = NULL; - } - - void clampCurrentValue(NSUInteger clamp) - { - if (kPOPAnimationClampNone == clamp) - return; - - // Clamp all vector values - CGFloat *currentValues = currentVec->data(); - const CGFloat *fromValues = fromVec->data(); - const CGFloat *toValues = toVec->data(); - - for (NSUInteger idx = 0; idx < valueCount; idx++) { - clampValue(currentValues[idx], fromValues[idx], toValues[idx], clamp); - } - } - - void clampCurrentValue() - { - clampCurrentValue(clampMode); - } -}; - -typedef struct _POPPropertyAnimationState POPPropertyAnimationState; - -@interface POPPropertyAnimation () - -@end - diff --git a/Pods/pop/pop/POPSpringAnimation.h b/Pods/pop/pop/POPSpringAnimation.h deleted file mode 100644 index a22cd5b..0000000 --- a/Pods/pop/pop/POPSpringAnimation.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -/** - @abstract A concrete spring animation class. - @discussion Animation is achieved through modeling spring dynamics. - */ -@interface POPSpringAnimation : POPPropertyAnimation - -/** - @abstract The designated initializer. - @returns An instance of a spring animation. - */ -+ (instancetype)animation; - -/** - @abstract Convenience initializer that returns an animation with animatable property of name. - @param name The name of the animatable property. - @returns An instance of a spring animation configured with specified animatable property. - */ -+ (instancetype)animationWithPropertyNamed:(NSString *)name; - -/** - @abstract The current velocity value. - @discussion Set before animation start to account for initial velocity. Expressed in change of value units per second. - */ -@property (copy, nonatomic) id velocity; - -/** - @abstract The effective bounciness. - @discussion Use in conjunction with 'springSpeed' to change animation effect. Values are converted into corresponding dynamics constants. Higher values increase spring movement range resulting in more oscillations and springiness. Defined as a value in the range [0, 20]. Defaults to 4. - */ -@property (assign, nonatomic) CGFloat springBounciness; - -/** - @abstract The effective speed. - @discussion Use in conjunction with 'springBounciness' to change animation effect. Values are converted into corresponding dynamics constants. Higher values increase the dampening power of the spring resulting in a faster initial velocity and more rapid bounce slowdown. Defined as a value in the range [0, 20]. Defaults to 12. - */ -@property (assign, nonatomic) CGFloat springSpeed; - -/** - @abstract The tension used in the dynamics simulation. - @discussion Can be used over bounciness and speed for finer grain tweaking of animation effect. - */ -@property (assign, nonatomic) CGFloat dynamicsTension; - -/** - @abstract The friction used in the dynamics simulation. - @discussion Can be used over bounciness and speed for finer grain tweaking of animation effect. - */ -@property (assign, nonatomic) CGFloat dynamicsFriction; - -/** - @abstract The mass used in the dynamics simulation. - @discussion Can be used over bounciness and speed for finer grain tweaking of animation effect. - */ -@property (assign, nonatomic) CGFloat dynamicsMass; - -@end diff --git a/Pods/pop/pop/POPSpringAnimation.mm b/Pods/pop/pop/POPSpringAnimation.mm deleted file mode 100644 index d299770..0000000 --- a/Pods/pop/pop/POPSpringAnimation.mm +++ /dev/null @@ -1,192 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPSpringAnimationInternal.h" - -@implementation POPSpringAnimation - -#pragma mark - Lifecycle - -#undef __state -#define __state ((POPSpringAnimationState *)_state) - -+ (instancetype)animation -{ - return [[self alloc] init]; -} - -+ (instancetype)animationWithPropertyNamed:(NSString *)aName -{ - POPSpringAnimation *anim = [self animation]; - anim.property = [POPAnimatableProperty propertyWithName:aName]; - return anim; -} - -- (void)_initState -{ - _state = new POPSpringAnimationState(self); -} - -- (id)init -{ - self = [super _init]; - if (nil != self) { - __state->solver = new SpringSolver4d(1, 1, 1); - __state->updatedDynamicsThreshold(); - __state->updatedBouncinessAndSpeed(); - } - return self; -} - -- (void)dealloc -{ - if (__state) { - delete __state->solver; - __state->solver = NULL; - } -} - -#pragma mark - Properties - -- (id)velocity -{ - return POPBox(__state->velocityVec, __state->valueType); -} - -- (void)setVelocity:(id)aValue -{ - POPPropertyAnimationState *s = __state; - VectorRef vec = POPUnbox(aValue, s->valueType, s->valueCount, YES); - VectorRef origVec = POPUnbox(aValue, s->valueType, s->valueCount, YES); - if (!vec_equal(vec, s->velocityVec)) { - s->velocityVec = vec; - s->originalVelocityVec = origVec; - - if (s->tracing) { - [s->tracer updateVelocity:aValue]; - } - } -} - -DEFINE_RW_PROPERTY(POPSpringAnimationState, dynamicsTension, setDynamicsTension:, CGFloat, [self _updatedDynamicsTension];); -DEFINE_RW_PROPERTY(POPSpringAnimationState, dynamicsFriction, setDynamicsFriction:, CGFloat, [self _updatedDynamicsFriction];); -DEFINE_RW_PROPERTY(POPSpringAnimationState, dynamicsMass, setDynamicsMass:, CGFloat, [self _updatedDynamicsMass];); - -FB_PROPERTY_GET(POPSpringAnimationState, springSpeed, CGFloat); -- (void)setSpringSpeed:(CGFloat)aFloat -{ - POPSpringAnimationState *s = __state; - if (s->userSpecifiedDynamics || aFloat != s->springSpeed) { - s->springSpeed = aFloat; - s->userSpecifiedDynamics = false; - s->updatedBouncinessAndSpeed(); - if (s->tracing) { - [s->tracer updateSpeed:aFloat]; - } - } -} - -FB_PROPERTY_GET(POPSpringAnimationState, springBounciness, CGFloat); -- (void)setSpringBounciness:(CGFloat)aFloat -{ - POPSpringAnimationState *s = __state; - if (s->userSpecifiedDynamics || aFloat != s->springBounciness) { - s->springBounciness = aFloat; - s->userSpecifiedDynamics = false; - s->updatedBouncinessAndSpeed(); - if (s->tracing) { - [s->tracer updateBounciness:aFloat]; - } - } -} - -- (SpringSolver4d *)solver -{ - return __state->solver; -} - -- (void)setSolver:(SpringSolver4d *)aSolver -{ - if (aSolver != __state->solver) { - if (__state->solver) { - delete(__state->solver); - } - __state->solver = aSolver; - } -} - -#pragma mark - Utility - -- (void)_updatedDynamicsTension -{ - __state->userSpecifiedDynamics = true; - if(__state->tracing) { - [__state->tracer updateTension:__state->dynamicsTension]; - } - __state->updatedDynamics(); -} - -- (void)_updatedDynamicsFriction -{ - __state->userSpecifiedDynamics = true; - if(__state->tracing) { - [__state->tracer updateFriction:__state->dynamicsFriction]; - } - __state->updatedDynamics(); -} - -- (void)_updatedDynamicsMass -{ - __state->userSpecifiedDynamics = true; - if(__state->tracing) { - [__state->tracer updateMass:__state->dynamicsMass]; - } - __state->updatedDynamics(); -} - -- (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug -{ - [super _appendDescription:s debug:debug]; - - if (debug) { - if (_state->userSpecifiedDynamics) { - [s appendFormat:@"; dynamics = (tension:%f, friction:%f, mass:%f)", __state->dynamicsTension, __state->dynamicsFriction, __state->dynamicsMass]; - } else { - [s appendFormat:@"; bounciness = %f; speed = %f", __state->springBounciness, __state->springSpeed]; - } - } -} - -@end - -@implementation POPSpringAnimation (NSCopying) - -- (instancetype)copyWithZone:(NSZone *)zone { - - POPSpringAnimation *copy = [super copyWithZone:zone]; - - if (copy) { - id velocity = POPBox(__state->originalVelocityVec, __state->valueType); - - // If velocity never gets set, then POPBox will return nil, messing up __state->valueCount. - if (velocity) { - copy.velocity = velocity; - } - - copy.springBounciness = self.springBounciness; - copy.springSpeed = self.springSpeed; - copy.dynamicsTension = self.dynamicsTension; - copy.dynamicsFriction = self.dynamicsFriction; - copy.dynamicsMass = self.dynamicsMass; - } - - return copy; -} - -@end \ No newline at end of file diff --git a/Pods/pop/pop/POPSpringAnimationInternal.h b/Pods/pop/pop/POPSpringAnimationInternal.h deleted file mode 100644 index 6a72a43..0000000 --- a/Pods/pop/pop/POPSpringAnimationInternal.h +++ /dev/null @@ -1,132 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import "POPAnimationExtras.h" -#import "POPPropertyAnimationInternal.h" - -struct _POPSpringAnimationState : _POPPropertyAnimationState -{ - SpringSolver4d *solver; - CGFloat springSpeed; - CGFloat springBounciness; // normalized springiness - CGFloat dynamicsTension; // tension - CGFloat dynamicsFriction; // friction - CGFloat dynamicsMass; // mass - - _POPSpringAnimationState(id __unsafe_unretained anim) : _POPPropertyAnimationState(anim), - solver(nullptr), - springSpeed(12.), - springBounciness(4.), - dynamicsTension(0), - dynamicsFriction(0), - dynamicsMass(0) - { - type = kPOPAnimationSpring; - } - - bool hasConverged() - { - NSUInteger count = valueCount; - if (shouldRound()) { - return vec_equal(previous2Vec, previousVec) && vec_equal(previousVec, toVec); - } else { - if (!previousVec || !previous2Vec) - return false; - - CGFloat t = dynamicsThreshold / 5; - - const CGFloat *toValues = toVec->data(); - const CGFloat *previousValues = previousVec->data(); - const CGFloat *previous2Values = previous2Vec->data(); - - for (NSUInteger idx = 0; idx < count; idx++) { - if ((std::abs(toValues[idx] - previousValues[idx]) >= t) || (std::abs(previous2Values[idx] - previousValues[idx]) >= t)) { - return false; - } - } - return true; - } - } - - bool isDone() { - if (_POPPropertyAnimationState::isDone()) { - return true; - } - return solver->started() && (hasConverged() || solver->hasConverged()); - } - - void updatedDynamics() - { - if (NULL != solver) { - solver->setConstants(dynamicsTension, dynamicsFriction, dynamicsMass); - } - } - - void updatedDynamicsThreshold() - { - _POPPropertyAnimationState::updatedDynamicsThreshold(); - if (NULL != solver) { - solver->setThreshold(dynamicsThreshold); - } - } - - void updatedBouncinessAndSpeed() { - [POPSpringAnimation convertBounciness:springBounciness speed:springSpeed toTension:&dynamicsTension friction:&dynamicsFriction mass:&dynamicsMass]; - updatedDynamics(); - } - - bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) { - // advance past not yet initialized animations - if (NULL == currentVec) { - return false; - } - - CFTimeInterval localTime = time - startTime; - - Vector4d value = vector4d(currentVec); - Vector4d toValue = vector4d(toVec); - Vector4d velocity = vector4d(velocityVec); - - SSState4d state; - state.p = toValue - value; - - // the solver assumes a spring of size zero - // flip the velocity from user perspective to solver perspective - state.v = velocity * -1; - - solver->advance(state, localTime, dt); - value = toValue - state.p; - - // flip velocity back to user perspective - velocity = state.v * -1; - - *currentVec = value; - - if (velocityVec) { - *velocityVec = velocity; - } - - clampCurrentValue(); - - return true; - } - - virtual void reset(bool all) { - _POPPropertyAnimationState::reset(all); - - if (solver) { - solver->setConstants(dynamicsTension, dynamicsFriction, dynamicsMass); - solver->reset(); - } - } -}; - -typedef struct _POPSpringAnimationState POPSpringAnimationState; diff --git a/Pods/pop/pop/POPSpringSolver.h b/Pods/pop/pop/POPSpringSolver.h deleted file mode 100644 index df485bf..0000000 --- a/Pods/pop/pop/POPSpringSolver.h +++ /dev/null @@ -1,190 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import "POPVector.h" - -namespace POP { - - template - struct SSState - { - T p; - T v; - }; - - template - struct SSDerivative - { - T dp; - T dv; - }; - - typedef SSState SSState4d; - typedef SSDerivative SSDerivative4d; - - const CFTimeInterval solverDt = 0.001f; - const CFTimeInterval maxSolverDt = 30.0f; - - /** - Templated spring solver class. - */ - template - class SpringSolver - { - double _k; // stiffness - double _b; // dampening - double _m; // mass - - double _tp; // threshold - double _tv; // threshold velocity - double _ta; // threshold acceleration - - CFTimeInterval _accumulatedTime; - SSState _lastState; - T _lastDv; - bool _started; - - public: - SpringSolver(double k, double b, double m = 1) : _k(k), _b(b), _m(m), _started(false) - { - _accumulatedTime = 0; - _lastState.p = T::Zero(); - _lastState.v = T::Zero(); - _lastDv = T::Zero(); - setThreshold(1.); - } - - ~SpringSolver() - { - } - - bool started() - { - return _started; - } - - void setConstants(double k, double b, double m) - { - _k = k; - _b = b; - _m = m; - } - - void setThreshold(double t) - { - _tp = t / 2; // half a unit - _tv = 25.0 * t; // 5 units per second, squared for comparison - _ta = 625.0 * t * t; // 5 units per second squared, squared for comparison - } - - T acceleration(const SSState &state, double t) - { - return state.p*(-_k/_m) - state.v*(_b/_m); - } - - SSDerivative evaluate(const SSState &initial, double t) - { - SSDerivative output; - output.dp = initial.v; - output.dv = acceleration(initial, t); - return output; - } - - SSDerivative evaluate(const SSState &initial, double t, double dt, const SSDerivative &d) - { - SSState state; - state.p = initial.p + d.dp*dt; - state.v = initial.v + d.dv*dt; - SSDerivative output; - output.dp = state.v; - output.dv = acceleration(state, t+dt); - return output; - } - - void integrate(SSState &state, double t, double dt) - { - SSDerivative a = evaluate(state, t); - SSDerivative b = evaluate(state, t, dt*0.5, a); - SSDerivative c = evaluate(state, t, dt*0.5, b); - SSDerivative d = evaluate(state, t, dt, c); - - T dpdt = (a.dp + (b.dp + c.dp)*2.0 + d.dp) * (1.0/6.0); - T dvdt = (a.dv + (b.dv + c.dv)*2.0 + d.dv) * (1.0/6.0); - - state.p = state.p + dpdt*dt; - state.v = state.v + dvdt*dt; - - _lastDv = dvdt; - } - - SSState interpolate(const SSState &previous, const SSState ¤t, double alpha) - { - SSState state; - state.p = current.p*alpha + previous.p*(1-alpha); - state.v = current.v*alpha + previous.v*(1-alpha); - return state; - } - - void advance(SSState &state, double t, double dt) - { - _started = true; - - if (dt > maxSolverDt) { - // excessive time step, force shut down - _lastDv = _lastState.v = _lastState.p = T::Zero(); - } else { - _accumulatedTime += dt; - - SSState previousState = state, currentState = state; - while (_accumulatedTime >= solverDt) { - previousState = currentState; - this->integrate(currentState, t, solverDt); - t += solverDt; - _accumulatedTime -= solverDt; - } - CFTimeInterval alpha = _accumulatedTime / solverDt; - _lastState = state = this->interpolate(previousState, currentState, alpha); - } - } - - bool hasConverged() - { - if (!_started) { - return false; - } - - for (size_t idx = 0; idx < _lastState.p.size(); idx++) { - if (fabs(_lastState.p(idx)) >= _tp) { - return false; - } - } - - return (_lastState.v.squaredNorm() < _tv) && (_lastDv.squaredNorm() < _ta); - } - - void reset() - { - _accumulatedTime = 0; - _lastState.p = T::Zero(); - _lastState.v = T::Zero(); - _lastDv = T::Zero(); - _started = false; - } - }; - - /** - Convenience spring solver type definitions. - */ - typedef SpringSolver SpringSolver2d; - typedef SpringSolver SpringSolver3d; - typedef SpringSolver SpringSolver4d; -} - diff --git a/Pods/pop/pop/POPVector.h b/Pods/pop/pop/POPVector.h deleted file mode 100644 index 44d4e9f..0000000 --- a/Pods/pop/pop/POPVector.h +++ /dev/null @@ -1,394 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#ifndef __POP__FBVector__ -#define __POP__FBVector__ - -#include -#include - -#import - -#import - -#import "POPDefines.h" - -#if SCENEKIT_SDK_AVAILABLE -#import -#endif - -#if TARGET_OS_IPHONE -#import -#endif - -#import "POPMath.h" - -namespace POP { - - /** Fixed two-size vector class */ - template - struct Vector2 - { - private: - typedef T Vector2::* const _data[2]; - static const _data _v; - - public: - T x; - T y; - - // Zero vector - static const Vector2 Zero() { return Vector2(0); } - - // Constructors - Vector2() {} - explicit Vector2(T v) { x = v; y = v; }; - explicit Vector2(T x0, T y0) : x(x0), y(y0) {}; - explicit Vector2(const CGPoint &p) : x(p.x), y (p.y) {} - explicit Vector2(const CGSize &s) : x(s.width), y (s.height) {} - - // Copy constructor - template explicit Vector2(const Vector2 &v) : x(v.x), y(v.y) {} - - // Index operators - const T& operator[](size_t i) const { return this->*_v[i]; } - T& operator[](size_t i) { return this->*_v[i]; } - const T& operator()(size_t i) const { return this->*_v[i]; } - T& operator()(size_t i) { return this->*_v[i]; } - - // Backing data - T * data() { return &(this->*_v[0]); } - const T * data() const { return &(this->*_v[0]); } - - // Size - inline size_t size() const { return 2; } - - // Assignment - Vector2 &operator= (T v) { x = v; y = v; return *this;} - template Vector2 &operator= (const Vector2 &v) { x = v.x; y = v.y; return *this;} - - // Negation - Vector2 operator- (void) const { return Vector2(-x, -y); } - - // Equality - bool operator== (T v) const { return (x == v && y == v); } - bool operator== (const Vector2 &v) const { return (x == v.x && y == v.y); } - - // Inequality - bool operator!= (T v) const {return (x != v || y != v); } - bool operator!= (const Vector2 &v) const { return (x != v.x || y != v.y); } - - // Scalar Math - Vector2 operator+ (T v) const { return Vector2(x + v, y + v); } - Vector2 operator- (T v) const { return Vector2(x - v, y - v); } - Vector2 operator* (T v) const { return Vector2(x * v, y * v); } - Vector2 operator/ (T v) const { return Vector2(x / v, y / v); } - Vector2 &operator+= (T v) { x += v; y += v; return *this; }; - Vector2 &operator-= (T v) { x -= v; y -= v; return *this; }; - Vector2 &operator*= (T v) { x *= v; y *= v; return *this; }; - Vector2 &operator/= (T v) { x /= v; y /= v; return *this; }; - - // Vector Math - Vector2 operator+ (const Vector2 &v) const { return Vector2(x + v.x, y + v.y); } - Vector2 operator- (const Vector2 &v) const { return Vector2(x - v.x, y - v.y); } - Vector2 &operator+= (const Vector2 &v) { x += v.x; y += v.y; return *this; }; - Vector2 &operator-= (const Vector2 &v) { x -= v.x; y -= v.y; return *this; }; - - // Norms - CGFloat norm() const { return sqrtr(squaredNorm()); } - CGFloat squaredNorm() const { return x * x + y * y; } - - // Cast - template Vector2 cast() const { return Vector2(x, y); } - CGPoint cg_point() const { return CGPointMake(x, y); }; - }; - - template - const typename Vector2::_data Vector2::_v = { &Vector2::x, &Vector2::y }; - - /** Fixed three-size vector class */ - template - struct Vector3 - { - private: - typedef T Vector3::* const _data[3]; - static const _data _v; - - public: - T x; - T y; - T z; - - // Zero vector - static const Vector3 Zero() { return Vector3(0); }; - - // Constructors - Vector3() {} - explicit Vector3(T v) : x(v), y(v), z(v) {}; - explicit Vector3(T x0, T y0, T z0) : x(x0), y(y0), z(z0) {}; - - // Copy constructor - template explicit Vector3(const Vector3 &v) : x(v.x), y(v.y), z(v.z) {} - - // Index operators - const T& operator[](size_t i) const { return this->*_v[i]; } - T& operator[](size_t i) { return this->*_v[i]; } - const T& operator()(size_t i) const { return this->*_v[i]; } - T& operator()(size_t i) { return this->*_v[i]; } - - // Backing data - T * data() { return &(this->*_v[0]); } - const T * data() const { return &(this->*_v[0]); } - - // Size - inline size_t size() const { return 3; } - - // Assignment - Vector3 &operator= (T v) { x = v; y = v; z = v; return *this;} - template Vector3 &operator= (const Vector3 &v) { x = v.x; y = v.y; z = v.z; return *this;} - - // Negation - Vector3 operator- (void) const { return Vector3(-x, -y, -z); } - - // Equality - bool operator== (T v) const { return (x == v && y == v && z = v); } - bool operator== (const Vector3 &v) const { return (x == v.x && y == v.y && z == v.z); } - - // Inequality - bool operator!= (T v) const {return (x != v || y != v || z != v); } - bool operator!= (const Vector3 &v) const { return (x != v.x || y != v.y || z != v.z); } - - // Scalar Math - Vector3 operator+ (T v) const { return Vector3(x + v, y + v, z + v); } - Vector3 operator- (T v) const { return Vector3(x - v, y - v, z - v); } - Vector3 operator* (T v) const { return Vector3(x * v, y * v, z * v); } - Vector3 operator/ (T v) const { return Vector3(x / v, y / v, z / v); } - Vector3 &operator+= (T v) { x += v; y += v; z += v; return *this; }; - Vector3 &operator-= (T v) { x -= v; y -= v; z -= v; return *this; }; - Vector3 &operator*= (T v) { x *= v; y *= v; z *= v; return *this; }; - Vector3 &operator/= (T v) { x /= v; y /= v; z /= v; return *this; }; - - // Vector Math - Vector3 operator+ (const Vector3 &v) const { return Vector3(x + v.x, y + v.y, z + v.z); } - Vector3 operator- (const Vector3 &v) const { return Vector3(x - v.x, y - v.y, z - v.z); } - Vector3 &operator+= (const Vector3 &v) { x += v.x; y += v.y; z += v.z; return *this; }; - Vector3 &operator-= (const Vector3 &v) { x -= v.x; y -= v.y; z -= v.z; return *this; }; - - // Norms - CGFloat norm() const { return sqrtr(squaredNorm()); } - CGFloat squaredNorm() const { return x * x + y * y + z * z; } - - // Cast - template Vector3 cast() const { return Vector3(x, y, z); } - }; - - template - const typename Vector3::_data Vector3::_v = { &Vector3::x, &Vector3::y, &Vector3::z }; - - /** Fixed four-size vector class */ - template - struct Vector4 - { - private: - typedef T Vector4::* const _data[4]; - static const _data _v; - - public: - T x; - T y; - T z; - T w; - - // Zero vector - static const Vector4 Zero() { return Vector4(0); }; - - // Constructors - Vector4() {} - explicit Vector4(T v) : x(v), y(v), z(v), w(v) {}; - explicit Vector4(T x0, T y0, T z0, T w0) : x(x0), y(y0), z(z0), w(w0) {}; - - // Copy constructor - template explicit Vector4(const Vector4 &v) : x(v.x), y(v.y), z(v.z), w(v.w) {} - - // Index operators - const T& operator[](size_t i) const { return this->*_v[i]; } - T& operator[](size_t i) { return this->*_v[i]; } - const T& operator()(size_t i) const { return this->*_v[i]; } - T& operator()(size_t i) { return this->*_v[i]; } - - // Backing data - T * data() { return &(this->*_v[0]); } - const T * data() const { return &(this->*_v[0]); } - - // Size - inline size_t size() const { return 4; } - - // Assignment - Vector4 &operator= (T v) { x = v; y = v; z = v; w = v; return *this;} - template Vector4 &operator= (const Vector4 &v) { x = v.x; y = v.y; z = v.z; w = v.w; return *this;} - - // Negation - Vector4 operator- (void) const { return Vector4(-x, -y, -z, -w); } - - // Equality - bool operator== (T v) const { return (x == v && y == v && z = v, w = v); } - bool operator== (const Vector4 &v) const { return (x == v.x && y == v.y && z == v.z && w == v.w); } - - // Inequality - bool operator!= (T v) const {return (x != v || y != v || z != v || w != v); } - bool operator!= (const Vector4 &v) const { return (x != v.x || y != v.y || z != v.z || w != v.w); } - - // Scalar Math - Vector4 operator+ (T v) const { return Vector4(x + v, y + v, z + v, w + v); } - Vector4 operator- (T v) const { return Vector4(x - v, y - v, z - v, w - v); } - Vector4 operator* (T v) const { return Vector4(x * v, y * v, z * v, w * v); } - Vector4 operator/ (T v) const { return Vector4(x / v, y / v, z / v, w / v); } - Vector4 &operator+= (T v) { x += v; y += v; z += v; w += v; return *this; }; - Vector4 &operator-= (T v) { x -= v; y -= v; z -= v; w -= v; return *this; }; - Vector4 &operator*= (T v) { x *= v; y *= v; z *= v; w *= v; return *this; }; - Vector4 &operator/= (T v) { x /= v; y /= v; z /= v; w /= v; return *this; }; - - // Vector Math - Vector4 operator+ (const Vector4 &v) const { return Vector4(x + v.x, y + v.y, z + v.z, w + v.w); } - Vector4 operator- (const Vector4 &v) const { return Vector4(x - v.x, y - v.y, z - v.z, w - v.w); } - Vector4 &operator+= (const Vector4 &v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; }; - Vector4 &operator-= (const Vector4 &v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; }; - - // Norms - CGFloat norm() const { return sqrtr(squaredNorm()); } - CGFloat squaredNorm() const { return x * x + y * y + z * z + w * w; } - - // Cast - template Vector4 cast() const { return Vector4(x, y, z, w); } - }; - - template - const typename Vector4::_data Vector4::_v = { &Vector4::x, &Vector4::y, &Vector4::z, &Vector4::w }; - - /** Convenience typedefs */ - typedef Vector2 Vector2f; - typedef Vector2 Vector2d; - typedef Vector2 Vector2r; - typedef Vector3 Vector3f; - typedef Vector3 Vector3d; - typedef Vector3 Vector3r; - typedef Vector4 Vector4f; - typedef Vector4 Vector4d; - typedef Vector4 Vector4r; - - /** Variable-sized vector class */ - class Vector - { - size_t _count; - CGFloat *_values; - - private: - Vector(size_t); - Vector(const Vector& other); - - public: - ~Vector(); - - // Creates a new vector instance of count with values. Initializing a vector of size 0 returns NULL. - static Vector *new_vector(NSUInteger count, const CGFloat *values); - - // Creates a new vector given a pointer to another. Can return NULL. - static Vector *new_vector(const Vector * const other); - - // Creates a variable size vector given a static vector and count. - static Vector *new_vector(NSUInteger count, Vector4r vec); - - // Size of vector - NSUInteger size() const { return _count; } - - // Returns array of values - CGFloat *data () { return _values; } - const CGFloat *data () const { return _values; }; - - // Vector2r support - Vector2r vector2r() const; - - // Vector4r support - Vector4r vector4r() const; - - // CGFloat support - static Vector *new_cg_float(CGFloat f); - - // CGPoint support - CGPoint cg_point() const; - static Vector *new_cg_point(const CGPoint &p); - - // CGSize support - CGSize cg_size() const; - static Vector *new_cg_size(const CGSize &s); - - // CGRect support - CGRect cg_rect() const; - static Vector *new_cg_rect(const CGRect &r); - -#if TARGET_OS_IPHONE - // UIEdgeInsets support - UIEdgeInsets ui_edge_insets() const; - static Vector *new_ui_edge_insets(const UIEdgeInsets &i); -#endif - - // CGAffineTransform support - CGAffineTransform cg_affine_transform() const; - static Vector *new_cg_affine_transform(const CGAffineTransform &t); - - // CGColorRef support - CGColorRef cg_color() const CF_RETURNS_RETAINED; - static Vector *new_cg_color(CGColorRef color); - -#if SCENEKIT_SDK_AVAILABLE - // SCNVector3 support - SCNVector3 scn_vector3() const; - static Vector *new_scn_vector3(const SCNVector3 &vec3); - - // SCNVector4 support - SCNVector4 scn_vector4() const; - static Vector *new_scn_vector4(const SCNVector4 &vec4); -#endif - - // operator overloads - CGFloat &operator[](size_t i) const { - NSCAssert(size() > i, @"unexpected vector size:%lu", (unsigned long)size()); - return _values[i]; - } - - // Returns the mathematical length - CGFloat norm() const; - CGFloat squaredNorm() const; - - // Round to nearest sub - void subRound(CGFloat sub); - - // Returns string description - NSString * toString() const; - - // Operator overloads - template Vector& operator= (const Vector4& other) { - size_t count = MIN(_count, other.size()); - for (size_t i = 0; i < count; i++) { - _values[i] = other[i]; - } - return *this; - } - Vector& operator= (const Vector& other); - void swap(Vector &first, Vector &second); - bool operator==(const Vector &other) const; - bool operator!=(const Vector &other) const; - }; - - /** Convenience typedefs */ - typedef std::shared_ptr VectorRef; - typedef std::shared_ptr VectorConstRef; - -} -#endif /* defined(__POP__FBVector__) */ diff --git a/Pods/pop/pop/POPVector.mm b/Pods/pop/pop/POPVector.mm deleted file mode 100644 index 96cee24..0000000 --- a/Pods/pop/pop/POPVector.mm +++ /dev/null @@ -1,334 +0,0 @@ -/** - Copyright (c) 2014-present, Facebook, Inc. - All rights reserved. - - This source code is licensed under the BSD-style license found in the - LICENSE file in the root directory of this source tree. An additional grant - of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "POPVector.h" - -#import "POPDefines.h" -#import "POPCGUtils.h" - -namespace POP -{ - - Vector::Vector(const size_t count) - { - _count = count; - _values = 0 != count ? (CGFloat *)calloc(count, sizeof(CGFloat)) : NULL; - } - - Vector::Vector(const Vector& other) - { - _count = other.size(); - _values = 0 != _count ? (CGFloat *)calloc(_count, sizeof(CGFloat)) : NULL; - if (0 != _count) { - memcpy(_values, other.data(), _count * sizeof(CGFloat)); - } - } - - Vector::~Vector() - { - if (NULL != _values) { - free(_values); - _values = NULL; - } - _count = 0; - } - - void Vector::swap(Vector &first, Vector &second) - { - using std::swap; - swap(first._count, second._count); - swap(first._values, second._values); - } - - Vector& Vector::operator=(const Vector& other) - { - Vector temp(other); - swap(*this, temp); - return *this; - } - - bool Vector::operator==(const Vector &other) const { - if (_count != other.size()) { - return false; - } - - const CGFloat * const values = other.data(); - - for (NSUInteger idx = 0; idx < _count; idx++) { - if (_values[idx] != values[idx]) { - return false; - } - } - - return true; - } - - bool Vector::operator!=(const Vector &other) const { - if (_count == other.size()) { - return false; - } - - const CGFloat * const values = other.data(); - - for (NSUInteger idx = 0; idx < _count; idx++) { - if (_values[idx] != values[idx]) { - return false; - } - } - - return true; - } - - Vector *Vector::new_vector(NSUInteger count, const CGFloat *values) - { - if (0 == count) { - return NULL; - } - - Vector *v = new Vector(count); - if (NULL != values) { - memcpy(v->_values, values, count * sizeof(CGFloat)); - } - return v; - } - - Vector *Vector::new_vector(const Vector * const other) - { - if (NULL == other) { - return NULL; - } - - return Vector::new_vector(other->size(), other->data()); - } - - Vector *Vector::new_vector(NSUInteger count, Vector4r vec) - { - if (0 == count) { - return NULL; - } - - Vector *v = new Vector(count); - - NSCAssert(count <= 4, @"unexpected count %lu", (unsigned long)count); - for (NSUInteger i = 0; i < MIN(count, (NSUInteger)4); i++) { - v->_values[i] = vec[i]; - } - - return v; - } - - Vector4r Vector::vector4r() const - { - Vector4r v = Vector4r::Zero(); - for (size_t i = 0; i < _count; i++) { - v(i) = _values[i]; - } - return v; - } - - Vector2r Vector::vector2r() const - { - Vector2r v = Vector2r::Zero(); - if (_count > 0) v(0) = _values[0]; - if (_count > 1) v(1) = _values[1]; - return v; - } - - Vector *Vector::new_cg_float(CGFloat f) - { - Vector *v = new Vector(1); - v->_values[0] = f; - return v; - } - - CGPoint Vector::cg_point () const - { - Vector2r v = vector2r(); - return CGPointMake(v(0), v(1)); - } - - Vector *Vector::new_cg_point(const CGPoint &p) - { - Vector *v = new Vector(2); - v->_values[0] = p.x; - v->_values[1] = p.y; - return v; - } - - CGSize Vector::cg_size () const - { - Vector2r v = vector2r(); - return CGSizeMake(v(0), v(1)); - } - - Vector *Vector::new_cg_size(const CGSize &s) - { - Vector *v = new Vector(2); - v->_values[0] = s.width; - v->_values[1] = s.height; - return v; - } - - CGRect Vector::cg_rect() const - { - return _count < 4 ? CGRectZero : CGRectMake(_values[0], _values[1], _values[2], _values[3]); - } - - Vector *Vector::new_cg_rect(const CGRect &r) - { - Vector *v = new Vector(4); - v->_values[0] = r.origin.x; - v->_values[1] = r.origin.y; - v->_values[2] = r.size.width; - v->_values[3] = r.size.height; - return v; - } - -#if TARGET_OS_IPHONE - - UIEdgeInsets Vector::ui_edge_insets() const - { - return _count < 4 ? UIEdgeInsetsZero : UIEdgeInsetsMake(_values[0], _values[1], _values[2], _values[3]); - } - - Vector *Vector::new_ui_edge_insets(const UIEdgeInsets &i) - { - Vector *v = new Vector(4); - v->_values[0] = i.top; - v->_values[1] = i.left; - v->_values[2] = i.bottom; - v->_values[3] = i.right; - return v; - } - -#endif - - CGAffineTransform Vector::cg_affine_transform() const - { - if (_count < 6) { - return CGAffineTransformIdentity; - } - - NSCAssert(size() >= 6, @"unexpected vector size:%lu", (unsigned long)size()); - CGAffineTransform t; - t.a = _values[0]; - t.b = _values[1]; - t.c = _values[2]; - t.d = _values[3]; - t.tx = _values[4]; - t.ty = _values[5]; - return t; - } - - Vector *Vector::new_cg_affine_transform(const CGAffineTransform &t) - { - Vector *v = new Vector(6); - v->_values[0] = t.a; - v->_values[1] = t.b; - v->_values[2] = t.c; - v->_values[3] = t.d; - v->_values[4] = t.tx; - v->_values[5] = t.ty; - return v; - } - - CGColorRef Vector::cg_color() const - { - if (_count < 4) { - return NULL; - } - return POPCGColorRGBACreate(_values); - } - - Vector *Vector::new_cg_color(CGColorRef color) - { - CGFloat rgba[4]; - POPCGColorGetRGBAComponents(color, rgba); - return new_vector(4, rgba); - } - -#if SCENEKIT_SDK_AVAILABLE - SCNVector3 Vector::scn_vector3() const - { - return _count < 3 ? SCNVector3Make(0.0, 0.0, 0.0) : SCNVector3Make(_values[0], _values[1], _values[2]); - } - - Vector *Vector::new_scn_vector3(const SCNVector3 &vec3) - { - Vector *v = new Vector(3); - v->_values[0] = vec3.x; - v->_values[1] = vec3.y; - v->_values[2] = vec3.z; - return v; - } - - SCNVector4 Vector::scn_vector4() const - { - return _count < 4 ? SCNVector4Make(0.0, 0.0, 0.0, 0.0) : SCNVector4Make(_values[0], _values[1], _values[2], _values[3]); - } - - Vector *Vector::new_scn_vector4(const SCNVector4 &vec4) - { - Vector *v = new Vector(4); - v->_values[0] = vec4.x; - v->_values[1] = vec4.y; - v->_values[2] = vec4.z; - v->_values[3] = vec4.w; - return v; - } -#endif - - void Vector::subRound(CGFloat sub) - { - for (NSUInteger idx = 0; idx < _count; idx++) { - _values[idx] = POPSubRound(_values[idx], sub); - } - } - - CGFloat Vector::norm() const - { - return sqrtr(squaredNorm()); - } - - CGFloat Vector::squaredNorm() const - { - CGFloat d = 0; - for (NSUInteger idx = 0; idx < _count; idx++) { - d += (_values[idx] * _values[idx]); - } - return d; - } - - NSString * Vector::toString() const - { - if (0 == _count) - return @"()"; - - if (1 == _count) - return [NSString stringWithFormat:@"%f", _values[0]]; - - if (2 == _count) - return [NSString stringWithFormat:@"(%.3f, %.3f)", _values[0], _values[1]]; - - NSMutableString *s = [NSMutableString stringWithCapacity:10]; - - for (NSUInteger idx = 0; idx < _count; idx++) { - if (0 == idx) { - [s appendFormat:@"[%.3f", _values[idx]]; - } else if (idx == _count - 1) { - [s appendFormat:@", %.3f]", _values[idx]]; - } else { - [s appendFormat:@", %.3f", _values[idx]]; - } - } - - return s; - - } -} diff --git a/Pods/pop/pop/WebCore/FloatConversion.h b/Pods/pop/pop/WebCore/FloatConversion.h deleted file mode 100644 index 4a16166..0000000 --- a/Pods/pop/pop/WebCore/FloatConversion.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FloatConversion_h -#define FloatConversion_h - -#include - -namespace WebCore { - - template - float narrowPrecisionToFloat(T); - - template<> - inline float narrowPrecisionToFloat(double number) - { - return static_cast(number); - } - - template - CGFloat narrowPrecisionToCGFloat(T); - - template<> - inline CGFloat narrowPrecisionToCGFloat(double number) - { - return static_cast(number); - } - -} // namespace WebCore - -#endif // FloatConversion_h diff --git a/Pods/pop/pop/WebCore/TransformationMatrix.cpp b/Pods/pop/pop/WebCore/TransformationMatrix.cpp deleted file mode 100644 index 7264ab5..0000000 --- a/Pods/pop/pop/WebCore/TransformationMatrix.cpp +++ /dev/null @@ -1,1074 +0,0 @@ -/* - * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2009 Torch Mobile, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "TransformationMatrix.h" - -#include - -#include "FloatConversion.h" - -inline double deg2rad(double d) { return d * M_PI / 180.0; } -inline double rad2deg(double r) { return r * 180.0 / M_PI; } -inline double deg2grad(double d) { return d * 400.0 / 360.0; } -inline double grad2deg(double g) { return g * 360.0 / 400.0; } -inline double turn2deg(double t) { return t * 360.0; } -inline double deg2turn(double d) { return d / 360.0; } -inline double rad2grad(double r) { return r * 200.0 / M_PI; } -inline double grad2rad(double g) { return g * M_PI / 200.0; } - -//using namespace std; - -namespace WebCore { - - // - // Supporting Math Functions - // - // This is a set of function from various places (attributed inline) to do things like - // inversion and decomposition of a 4x4 matrix. They are used throughout the code - // - - // - // Adapted from Matrix Inversion by Richard Carling, Graphics Gems . - - // EULA: The Graphics Gems code is copyright-protected. In other words, you cannot claim the text of the code - // as your own and resell it. Using the code is permitted in any program, product, or library, non-commercial - // or commercial. Giving credit is not required, though is a nice gesture. The code comes as-is, and if there - // are any flaws or problems with any Gems code, nobody involved with Gems - authors, editors, publishers, or - // webmasters - are to be held responsible. Basically, don't be a jerk, and remember that anything free comes - // with no guarantee. - - // A clarification about the storage of matrix elements - // - // This class uses a 2 dimensional array internally to store the elements of the matrix. The first index into - // the array refers to the column that the element lies in; the second index refers to the row. - // - // In other words, this is the layout of the matrix: - // - // | m_matrix[0][0] m_matrix[1][0] m_matrix[2][0] m_matrix[3][0] | - // | m_matrix[0][1] m_matrix[1][1] m_matrix[2][1] m_matrix[3][1] | - // | m_matrix[0][2] m_matrix[1][2] m_matrix[2][2] m_matrix[3][2] | - // | m_matrix[0][3] m_matrix[1][3] m_matrix[2][3] m_matrix[3][3] | - - typedef double Vector4[4]; - typedef double Vector3[3]; - - const double SMALL_NUMBER = 1.e-8; - - // inverse(original_matrix, inverse_matrix) - // - // calculate the inverse of a 4x4 matrix - // - // -1 - // A = ___1__ adjoint A - // det A - - // double = determinant2x2(double a, double b, double c, double d) - // - // calculate the determinant of a 2x2 matrix. - - static double determinant2x2(double a, double b, double c, double d) - { - return a * d - b * c; - } - - // double = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3) - // - // Calculate the determinant of a 3x3 matrix - // in the form - // - // | a1, b1, c1 | - // | a2, b2, c2 | - // | a3, b3, c3 | - - static double determinant3x3(double a1, double a2, double a3, double b1, double b2, double b3, double c1, double c2, double c3) - { - return a1 * determinant2x2(b2, b3, c2, c3) - - b1 * determinant2x2(a2, a3, c2, c3) - + c1 * determinant2x2(a2, a3, b2, b3); - } - - // double = determinant4x4(matrix) - // - // calculate the determinant of a 4x4 matrix. - - static double determinant4x4(const TransformationMatrix::Matrix4& m) - { - // Assign to individual variable names to aid selecting - // correct elements - - double a1 = m[0][0]; - double b1 = m[0][1]; - double c1 = m[0][2]; - double d1 = m[0][3]; - - double a2 = m[1][0]; - double b2 = m[1][1]; - double c2 = m[1][2]; - double d2 = m[1][3]; - - double a3 = m[2][0]; - double b3 = m[2][1]; - double c3 = m[2][2]; - double d3 = m[2][3]; - - double a4 = m[3][0]; - double b4 = m[3][1]; - double c4 = m[3][2]; - double d4 = m[3][3]; - - return a1 * determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) - - b1 * determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) - + c1 * determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) - - d1 * determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); - } - - // adjoint( original_matrix, inverse_matrix ) - // - // calculate the adjoint of a 4x4 matrix - // - // Let a denote the minor determinant of matrix A obtained by - // ij - // - // deleting the ith row and jth column from A. - // - // i+j - // Let b = (-1) a - // ij ji - // - // The matrix B = (b ) is the adjoint of A - // ij - - static void adjoint(const TransformationMatrix::Matrix4& matrix, TransformationMatrix::Matrix4& result) - { - // Assign to individual variable names to aid - // selecting correct values - double a1 = matrix[0][0]; - double b1 = matrix[0][1]; - double c1 = matrix[0][2]; - double d1 = matrix[0][3]; - - double a2 = matrix[1][0]; - double b2 = matrix[1][1]; - double c2 = matrix[1][2]; - double d2 = matrix[1][3]; - - double a3 = matrix[2][0]; - double b3 = matrix[2][1]; - double c3 = matrix[2][2]; - double d3 = matrix[2][3]; - - double a4 = matrix[3][0]; - double b4 = matrix[3][1]; - double c4 = matrix[3][2]; - double d4 = matrix[3][3]; - - // Row column labeling reversed since we transpose rows & columns - result[0][0] = determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4); - result[1][0] = - determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4); - result[2][0] = determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4); - result[3][0] = - determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); - - result[0][1] = - determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4); - result[1][1] = determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4); - result[2][1] = - determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4); - result[3][1] = determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4); - - result[0][2] = determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4); - result[1][2] = - determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4); - result[2][2] = determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4); - result[3][2] = - determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4); - - result[0][3] = - determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3); - result[1][3] = determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3); - result[2][3] = - determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3); - result[3][3] = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3); - } - - // Returns false if the matrix is not invertible - static bool inverse(const TransformationMatrix::Matrix4& matrix, TransformationMatrix::Matrix4& result) - { - // Calculate the adjoint matrix - adjoint(matrix, result); - - // Calculate the 4x4 determinant - // If the determinant is zero, - // then the inverse matrix is not unique. - double det = determinant4x4(matrix); - - if (fabs(det) < SMALL_NUMBER) - return false; - - // Scale the adjoint matrix to get the inverse - - for (int i = 0; i < 4; i++) - for (int j = 0; j < 4; j++) - result[i][j] = result[i][j] / det; - - return true; - } - - // End of code adapted from Matrix Inversion by Richard Carling - - // Perform a decomposition on the passed matrix, return false if unsuccessful - // From Graphics Gems: unmatrix.c - - // Transpose rotation portion of matrix a, return b - static void transposeMatrix4(const TransformationMatrix::Matrix4& a, TransformationMatrix::Matrix4& b) - { - for (int i = 0; i < 4; i++) - for (int j = 0; j < 4; j++) - b[i][j] = a[j][i]; - } - - // Multiply a homogeneous point by a matrix and return the transformed point - static void v4MulPointByMatrix(const Vector4 p, const TransformationMatrix::Matrix4& m, Vector4 result) - { - result[0] = (p[0] * m[0][0]) + (p[1] * m[1][0]) + - (p[2] * m[2][0]) + (p[3] * m[3][0]); - result[1] = (p[0] * m[0][1]) + (p[1] * m[1][1]) + - (p[2] * m[2][1]) + (p[3] * m[3][1]); - result[2] = (p[0] * m[0][2]) + (p[1] * m[1][2]) + - (p[2] * m[2][2]) + (p[3] * m[3][2]); - result[3] = (p[0] * m[0][3]) + (p[1] * m[1][3]) + - (p[2] * m[2][3]) + (p[3] * m[3][3]); - } - - static double v3Length(Vector3 a) - { - return sqrt((a[0] * a[0]) + (a[1] * a[1]) + (a[2] * a[2])); - } - - static void v3Scale(Vector3 v, double desiredLength) - { - double len = v3Length(v); - if (len != 0) { - double l = desiredLength / len; - v[0] *= l; - v[1] *= l; - v[2] *= l; - } - } - - static double v3Dot(const Vector3 a, const Vector3 b) - { - return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); - } - - // Make a linear combination of two vectors and return the result. - // result = (a * ascl) + (b * bscl) - static void v3Combine(const Vector3 a, const Vector3 b, Vector3 result, double ascl, double bscl) - { - result[0] = (ascl * a[0]) + (bscl * b[0]); - result[1] = (ascl * a[1]) + (bscl * b[1]); - result[2] = (ascl * a[2]) + (bscl * b[2]); - } - - // Return the cross product result = a cross b */ - static void v3Cross(const Vector3 a, const Vector3 b, Vector3 result) - { - result[0] = (a[1] * b[2]) - (a[2] * b[1]); - result[1] = (a[2] * b[0]) - (a[0] * b[2]); - result[2] = (a[0] * b[1]) - (a[1] * b[0]); - } - - static bool decompose(const TransformationMatrix::Matrix4& mat, TransformationMatrix::DecomposedType& result) - { - TransformationMatrix::Matrix4 localMatrix; - memcpy(localMatrix, mat, sizeof(TransformationMatrix::Matrix4)); - - // Normalize the matrix. - if (localMatrix[3][3] == 0) - return false; - - int i, j; - for (i = 0; i < 4; i++) - for (j = 0; j < 4; j++) - localMatrix[i][j] /= localMatrix[3][3]; - - // perspectiveMatrix is used to solve for perspective, but it also provides - // an easy way to test for singularity of the upper 3x3 component. - TransformationMatrix::Matrix4 perspectiveMatrix; - memcpy(perspectiveMatrix, localMatrix, sizeof(TransformationMatrix::Matrix4)); - for (i = 0; i < 3; i++) - perspectiveMatrix[i][3] = 0; - perspectiveMatrix[3][3] = 1; - - if (determinant4x4(perspectiveMatrix) == 0) - return false; - - // First, isolate perspective. This is the messiest. - if (localMatrix[0][3] != 0 || localMatrix[1][3] != 0 || localMatrix[2][3] != 0) { - // rightHandSide is the right hand side of the equation. - Vector4 rightHandSide; - rightHandSide[0] = localMatrix[0][3]; - rightHandSide[1] = localMatrix[1][3]; - rightHandSide[2] = localMatrix[2][3]; - rightHandSide[3] = localMatrix[3][3]; - - // Solve the equation by inverting perspectiveMatrix and multiplying - // rightHandSide by the inverse. (This is the easiest way, not - // necessarily the best.) - TransformationMatrix::Matrix4 inversePerspectiveMatrix, transposedInversePerspectiveMatrix; - inverse(perspectiveMatrix, inversePerspectiveMatrix); - transposeMatrix4(inversePerspectiveMatrix, transposedInversePerspectiveMatrix); - - Vector4 perspectivePoint; - v4MulPointByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspectivePoint); - - result.perspectiveX = perspectivePoint[0]; - result.perspectiveY = perspectivePoint[1]; - result.perspectiveZ = perspectivePoint[2]; - result.perspectiveW = perspectivePoint[3]; - - // Clear the perspective partition - localMatrix[0][3] = localMatrix[1][3] = localMatrix[2][3] = 0; - localMatrix[3][3] = 1; - } else { - // No perspective. - result.perspectiveX = result.perspectiveY = result.perspectiveZ = 0; - result.perspectiveW = 1; - } - - // Next take care of translation (easy). - result.translateX = localMatrix[3][0]; - localMatrix[3][0] = 0; - result.translateY = localMatrix[3][1]; - localMatrix[3][1] = 0; - result.translateZ = localMatrix[3][2]; - localMatrix[3][2] = 0; - - // Vector4 type and functions need to be added to the common set. - Vector3 row[3], pdum3; - - // Now get scale and shear. - for (i = 0; i < 3; i++) { - row[i][0] = localMatrix[i][0]; - row[i][1] = localMatrix[i][1]; - row[i][2] = localMatrix[i][2]; - } - - // Compute X scale factor and normalize first row. - result.scaleX = v3Length(row[0]); - v3Scale(row[0], 1.0); - - // Compute XY shear factor and make 2nd row orthogonal to 1st. - result.skewXY = v3Dot(row[0], row[1]); - v3Combine(row[1], row[0], row[1], 1.0, -result.skewXY); - - // Now, compute Y scale and normalize 2nd row. - result.scaleY = v3Length(row[1]); - v3Scale(row[1], 1.0); - result.skewXY /= result.scaleY; - - // Compute XZ and YZ shears, orthogonalize 3rd row. - result.skewXZ = v3Dot(row[0], row[2]); - v3Combine(row[2], row[0], row[2], 1.0, -result.skewXZ); - result.skewYZ = v3Dot(row[1], row[2]); - v3Combine(row[2], row[1], row[2], 1.0, -result.skewYZ); - - // Next, get Z scale and normalize 3rd row. - result.scaleZ = v3Length(row[2]); - v3Scale(row[2], 1.0); - result.skewXZ /= result.scaleZ; - result.skewYZ /= result.scaleZ; - - // At this point, the matrix (in rows[]) is orthonormal. - // Check for a coordinate system flip. If the determinant - // is -1, then negate the matrix and the scaling factors. - v3Cross(row[1], row[2], pdum3); - if (v3Dot(row[0], pdum3) < 0) { - - result.scaleX *= -1; - result.scaleY *= -1; - result.scaleZ *= -1; - - for (i = 0; i < 3; i++) { - row[i][0] *= -1; - row[i][1] *= -1; - row[i][2] *= -1; - } - } - - // Now, get the rotations out, as described in the gem. - - result.rotateY = asin(-row[0][2]); - if (cos(result.rotateY) != 0) { - result.rotateX = atan2(row[1][2], row[2][2]); - result.rotateZ = atan2(row[0][1], row[0][0]); - } else { - result.rotateX = atan2(-row[2][0], row[1][1]); - result.rotateZ = 0; - } - - double s, t, x, y, z, w; - - t = row[0][0] + row[1][1] + row[2][2] + 1.0; - - if (t > 1e-4) { - s = 0.5 / sqrt(t); - w = 0.25 / s; - x = (row[2][1] - row[1][2]) * s; - y = (row[0][2] - row[2][0]) * s; - z = (row[1][0] - row[0][1]) * s; - } else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) { - s = sqrt (1.0 + row[0][0] - row[1][1] - row[2][2]) * 2.0; // S=4*qx - x = 0.25 * s; - y = (row[0][1] + row[1][0]) / s; - z = (row[0][2] + row[2][0]) / s; - w = (row[2][1] - row[1][2]) / s; - } else if (row[1][1] > row[2][2]) { - s = sqrt (1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0; // S=4*qy - x = (row[0][1] + row[1][0]) / s; - y = 0.25 * s; - z = (row[1][2] + row[2][1]) / s; - w = (row[0][2] - row[2][0]) / s; - } else { - s = sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0; // S=4*qz - x = (row[0][2] + row[2][0]) / s; - y = (row[1][2] + row[2][1]) / s; - z = 0.25 * s; - w = (row[1][0] - row[0][1]) / s; - } - - result.quaternionX = x; - result.quaternionY = y; - result.quaternionZ = z; - result.quaternionW = w; - - return true; - } - - // Perform a spherical linear interpolation between the two - // passed quaternions with 0 <= t <= 1 - static void slerp(double qa[4], const double qb[4], double t) - { - double ax, ay, az, aw; - double bx, by, bz, bw; - double cx, cy, cz, cw; - double angle; - double th, invth, scale, invscale; - - ax = qa[0]; ay = qa[1]; az = qa[2]; aw = qa[3]; - bx = qb[0]; by = qb[1]; bz = qb[2]; bw = qb[3]; - - angle = ax * bx + ay * by + az * bz + aw * bw; - - if (angle < 0.0) { - ax = -ax; ay = -ay; - az = -az; aw = -aw; - angle = -angle; - } - - if (angle + 1.0 > .05) { - if (1.0 - angle >= .05) { - th = acos (angle); - invth = 1.0 / sin (th); - scale = sin (th * (1.0 - t)) * invth; - invscale = sin (th * t) * invth; - } else { - scale = 1.0 - t; - invscale = t; - } - } else { - bx = -ay; - by = ax; - bz = -aw; - bw = az; - scale = sin(M_PI * (.5 - t)); - invscale = sin (M_PI * t); - } - - cx = ax * scale + bx * invscale; - cy = ay * scale + by * invscale; - cz = az * scale + bz * invscale; - cw = aw * scale + bw * invscale; - - qa[0] = cx; qa[1] = cy; qa[2] = cz; qa[3] = cw; - } - - // End of Supporting Math Functions - - TransformationMatrix::TransformationMatrix(const CGAffineTransform& t) - { - setMatrix(t.a, t.b, t.c, t.d, t.tx, t.ty); - } - - TransformationMatrix::TransformationMatrix(const CATransform3D& t) - { - setMatrix( - t.m11, t.m12, t.m13, t.m14, - t.m21, t.m22, t.m23, t.m24, - t.m31, t.m32, t.m33, t.m34, - t.m41, t.m42, t.m43, t.m44); - } - - CATransform3D TransformationMatrix::transform3d() const - { - CATransform3D t; - t.m11 = narrowPrecisionToFloat(m11()); - t.m12 = narrowPrecisionToFloat(m12()); - t.m13 = narrowPrecisionToFloat(m13()); - t.m14 = narrowPrecisionToFloat(m14()); - t.m21 = narrowPrecisionToFloat(m21()); - t.m22 = narrowPrecisionToFloat(m22()); - t.m23 = narrowPrecisionToFloat(m23()); - t.m24 = narrowPrecisionToFloat(m24()); - t.m31 = narrowPrecisionToFloat(m31()); - t.m32 = narrowPrecisionToFloat(m32()); - t.m33 = narrowPrecisionToFloat(m33()); - t.m34 = narrowPrecisionToFloat(m34()); - t.m41 = narrowPrecisionToFloat(m41()); - t.m42 = narrowPrecisionToFloat(m42()); - t.m43 = narrowPrecisionToFloat(m43()); - t.m44 = narrowPrecisionToFloat(m44()); - return t; - } - - CGAffineTransform TransformationMatrix::affineTransform () const - { - CGAffineTransform t; - t.a = narrowPrecisionToFloat(m11()); - t.b = narrowPrecisionToFloat(m12()); - t.c = narrowPrecisionToFloat(m21()); - t.d = narrowPrecisionToFloat(m22()); - t.tx = narrowPrecisionToFloat(m41()); - t.ty = narrowPrecisionToFloat(m42()); - return t; - } - - TransformationMatrix::operator CATransform3D() const - { - return transform3d(); - } - - TransformationMatrix& TransformationMatrix::scale(double s) - { - return scaleNonUniform(s, s); - } - - TransformationMatrix& TransformationMatrix::rotateFromVector(double x, double y) - { - return rotate(rad2deg(atan2(y, x))); - } - - TransformationMatrix& TransformationMatrix::flipX() - { - return scaleNonUniform(-1.0, 1.0); - } - - TransformationMatrix& TransformationMatrix::flipY() - { - return scaleNonUniform(1.0, -1.0); - } - - TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx, double sy) - { - m_matrix[0][0] *= sx; - m_matrix[0][1] *= sx; - m_matrix[0][2] *= sx; - m_matrix[0][3] *= sx; - - m_matrix[1][0] *= sy; - m_matrix[1][1] *= sy; - m_matrix[1][2] *= sy; - m_matrix[1][3] *= sy; - return *this; - } - - TransformationMatrix& TransformationMatrix::scale3d(double sx, double sy, double sz) - { - scaleNonUniform(sx, sy); - - m_matrix[2][0] *= sz; - m_matrix[2][1] *= sz; - m_matrix[2][2] *= sz; - m_matrix[2][3] *= sz; - return *this; - } - - TransformationMatrix& TransformationMatrix::rotate3d(double x, double y, double z, double angle) - { - // Normalize the axis of rotation - double length = sqrt(x * x + y * y + z * z); - if (length == 0) { - // A direction vector that cannot be normalized, such as [0, 0, 0], will cause the rotation to not be applied. - return *this; - } else if (length != 1) { - x /= length; - y /= length; - z /= length; - } - - // Angles are in degrees. Switch to radians. - angle = deg2rad(angle); - - double sinTheta = sin(angle); - double cosTheta = cos(angle); - - TransformationMatrix mat; - - // Optimize cases where the axis is along a major axis - if (x == 1.0 && y == 0.0 && z == 0.0) { - mat.m_matrix[0][0] = 1.0; - mat.m_matrix[0][1] = 0.0; - mat.m_matrix[0][2] = 0.0; - mat.m_matrix[1][0] = 0.0; - mat.m_matrix[1][1] = cosTheta; - mat.m_matrix[1][2] = sinTheta; - mat.m_matrix[2][0] = 0.0; - mat.m_matrix[2][1] = -sinTheta; - mat.m_matrix[2][2] = cosTheta; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - } else if (x == 0.0 && y == 1.0 && z == 0.0) { - mat.m_matrix[0][0] = cosTheta; - mat.m_matrix[0][1] = 0.0; - mat.m_matrix[0][2] = -sinTheta; - mat.m_matrix[1][0] = 0.0; - mat.m_matrix[1][1] = 1.0; - mat.m_matrix[1][2] = 0.0; - mat.m_matrix[2][0] = sinTheta; - mat.m_matrix[2][1] = 0.0; - mat.m_matrix[2][2] = cosTheta; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - } else if (x == 0.0 && y == 0.0 && z == 1.0) { - mat.m_matrix[0][0] = cosTheta; - mat.m_matrix[0][1] = sinTheta; - mat.m_matrix[0][2] = 0.0; - mat.m_matrix[1][0] = -sinTheta; - mat.m_matrix[1][1] = cosTheta; - mat.m_matrix[1][2] = 0.0; - mat.m_matrix[2][0] = 0.0; - mat.m_matrix[2][1] = 0.0; - mat.m_matrix[2][2] = 1.0; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - } else { - // This case is the rotation about an arbitrary unit vector. - // - // Formula is adapted from Wikipedia article on Rotation matrix, - // http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle - // - // An alternate resource with the same matrix: http://www.fastgraph.com/makegames/3drotation/ - // - double oneMinusCosTheta = 1 - cosTheta; - mat.m_matrix[0][0] = cosTheta + x * x * oneMinusCosTheta; - mat.m_matrix[0][1] = y * x * oneMinusCosTheta + z * sinTheta; - mat.m_matrix[0][2] = z * x * oneMinusCosTheta - y * sinTheta; - mat.m_matrix[1][0] = x * y * oneMinusCosTheta - z * sinTheta; - mat.m_matrix[1][1] = cosTheta + y * y * oneMinusCosTheta; - mat.m_matrix[1][2] = z * y * oneMinusCosTheta + x * sinTheta; - mat.m_matrix[2][0] = x * z * oneMinusCosTheta + y * sinTheta; - mat.m_matrix[2][1] = y * z * oneMinusCosTheta - x * sinTheta; - mat.m_matrix[2][2] = cosTheta + z * z * oneMinusCosTheta; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - } - multiply(mat); - return *this; - } - - TransformationMatrix& TransformationMatrix::rotate3d(double rx, double ry, double rz) - { - // Angles are in degrees. Switch to radians. - rx = deg2rad(rx); - ry = deg2rad(ry); - rz = deg2rad(rz); - - TransformationMatrix mat; - - double sinTheta = sin(rz); - double cosTheta = cos(rz); - - mat.m_matrix[0][0] = cosTheta; - mat.m_matrix[0][1] = sinTheta; - mat.m_matrix[0][2] = 0.0; - mat.m_matrix[1][0] = -sinTheta; - mat.m_matrix[1][1] = cosTheta; - mat.m_matrix[1][2] = 0.0; - mat.m_matrix[2][0] = 0.0; - mat.m_matrix[2][1] = 0.0; - mat.m_matrix[2][2] = 1.0; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - - TransformationMatrix rmat(mat); - - sinTheta = sin(ry); - cosTheta = cos(ry); - - mat.m_matrix[0][0] = cosTheta; - mat.m_matrix[0][1] = 0.0; - mat.m_matrix[0][2] = -sinTheta; - mat.m_matrix[1][0] = 0.0; - mat.m_matrix[1][1] = 1.0; - mat.m_matrix[1][2] = 0.0; - mat.m_matrix[2][0] = sinTheta; - mat.m_matrix[2][1] = 0.0; - mat.m_matrix[2][2] = cosTheta; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - - rmat.multiply(mat); - - sinTheta = sin(rx); - cosTheta = cos(rx); - - mat.m_matrix[0][0] = 1.0; - mat.m_matrix[0][1] = 0.0; - mat.m_matrix[0][2] = 0.0; - mat.m_matrix[1][0] = 0.0; - mat.m_matrix[1][1] = cosTheta; - mat.m_matrix[1][2] = sinTheta; - mat.m_matrix[2][0] = 0.0; - mat.m_matrix[2][1] = -sinTheta; - mat.m_matrix[2][2] = cosTheta; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - - rmat.multiply(mat); - - multiply(rmat); - return *this; - } - - TransformationMatrix& TransformationMatrix::translate(double tx, double ty) - { - m_matrix[3][0] += tx * m_matrix[0][0] + ty * m_matrix[1][0]; - m_matrix[3][1] += tx * m_matrix[0][1] + ty * m_matrix[1][1]; - m_matrix[3][2] += tx * m_matrix[0][2] + ty * m_matrix[1][2]; - m_matrix[3][3] += tx * m_matrix[0][3] + ty * m_matrix[1][3]; - return *this; - } - - TransformationMatrix& TransformationMatrix::translate3d(double tx, double ty, double tz) - { - m_matrix[3][0] += tx * m_matrix[0][0] + ty * m_matrix[1][0] + tz * m_matrix[2][0]; - m_matrix[3][1] += tx * m_matrix[0][1] + ty * m_matrix[1][1] + tz * m_matrix[2][1]; - m_matrix[3][2] += tx * m_matrix[0][2] + ty * m_matrix[1][2] + tz * m_matrix[2][2]; - m_matrix[3][3] += tx * m_matrix[0][3] + ty * m_matrix[1][3] + tz * m_matrix[2][3]; - return *this; - } - - TransformationMatrix& TransformationMatrix::translateRight(double tx, double ty) - { - if (tx != 0) { - m_matrix[0][0] += m_matrix[0][3] * tx; - m_matrix[1][0] += m_matrix[1][3] * tx; - m_matrix[2][0] += m_matrix[2][3] * tx; - m_matrix[3][0] += m_matrix[3][3] * tx; - } - - if (ty != 0) { - m_matrix[0][1] += m_matrix[0][3] * ty; - m_matrix[1][1] += m_matrix[1][3] * ty; - m_matrix[2][1] += m_matrix[2][3] * ty; - m_matrix[3][1] += m_matrix[3][3] * ty; - } - - return *this; - } - - TransformationMatrix& TransformationMatrix::translateRight3d(double tx, double ty, double tz) - { - translateRight(tx, ty); - if (tz != 0) { - m_matrix[0][2] += m_matrix[0][3] * tz; - m_matrix[1][2] += m_matrix[1][3] * tz; - m_matrix[2][2] += m_matrix[2][3] * tz; - m_matrix[3][2] += m_matrix[3][3] * tz; - } - - return *this; - } - - TransformationMatrix& TransformationMatrix::skew(double sx, double sy) - { - // angles are in degrees. Switch to radians - sx = deg2rad(sx); - sy = deg2rad(sy); - - TransformationMatrix mat; - mat.m_matrix[0][1] = tan(sy); // note that the y shear goes in the first row - mat.m_matrix[1][0] = tan(sx); // and the x shear in the second row - - multiply(mat); - return *this; - } - - TransformationMatrix& TransformationMatrix::applyPerspective(double p) - { - TransformationMatrix mat; - if (p != 0) - mat.m_matrix[2][3] = -1/p; - - multiply(mat); - return *this; - } - - // this = mat * this. - TransformationMatrix& TransformationMatrix::multiply(const TransformationMatrix& mat) - { - Matrix4 tmp; - - tmp[0][0] = (mat.m_matrix[0][0] * m_matrix[0][0] + mat.m_matrix[0][1] * m_matrix[1][0] - + mat.m_matrix[0][2] * m_matrix[2][0] + mat.m_matrix[0][3] * m_matrix[3][0]); - tmp[0][1] = (mat.m_matrix[0][0] * m_matrix[0][1] + mat.m_matrix[0][1] * m_matrix[1][1] - + mat.m_matrix[0][2] * m_matrix[2][1] + mat.m_matrix[0][3] * m_matrix[3][1]); - tmp[0][2] = (mat.m_matrix[0][0] * m_matrix[0][2] + mat.m_matrix[0][1] * m_matrix[1][2] - + mat.m_matrix[0][2] * m_matrix[2][2] + mat.m_matrix[0][3] * m_matrix[3][2]); - tmp[0][3] = (mat.m_matrix[0][0] * m_matrix[0][3] + mat.m_matrix[0][1] * m_matrix[1][3] - + mat.m_matrix[0][2] * m_matrix[2][3] + mat.m_matrix[0][3] * m_matrix[3][3]); - - tmp[1][0] = (mat.m_matrix[1][0] * m_matrix[0][0] + mat.m_matrix[1][1] * m_matrix[1][0] - + mat.m_matrix[1][2] * m_matrix[2][0] + mat.m_matrix[1][3] * m_matrix[3][0]); - tmp[1][1] = (mat.m_matrix[1][0] * m_matrix[0][1] + mat.m_matrix[1][1] * m_matrix[1][1] - + mat.m_matrix[1][2] * m_matrix[2][1] + mat.m_matrix[1][3] * m_matrix[3][1]); - tmp[1][2] = (mat.m_matrix[1][0] * m_matrix[0][2] + mat.m_matrix[1][1] * m_matrix[1][2] - + mat.m_matrix[1][2] * m_matrix[2][2] + mat.m_matrix[1][3] * m_matrix[3][2]); - tmp[1][3] = (mat.m_matrix[1][0] * m_matrix[0][3] + mat.m_matrix[1][1] * m_matrix[1][3] - + mat.m_matrix[1][2] * m_matrix[2][3] + mat.m_matrix[1][3] * m_matrix[3][3]); - - tmp[2][0] = (mat.m_matrix[2][0] * m_matrix[0][0] + mat.m_matrix[2][1] * m_matrix[1][0] - + mat.m_matrix[2][2] * m_matrix[2][0] + mat.m_matrix[2][3] * m_matrix[3][0]); - tmp[2][1] = (mat.m_matrix[2][0] * m_matrix[0][1] + mat.m_matrix[2][1] * m_matrix[1][1] - + mat.m_matrix[2][2] * m_matrix[2][1] + mat.m_matrix[2][3] * m_matrix[3][1]); - tmp[2][2] = (mat.m_matrix[2][0] * m_matrix[0][2] + mat.m_matrix[2][1] * m_matrix[1][2] - + mat.m_matrix[2][2] * m_matrix[2][2] + mat.m_matrix[2][3] * m_matrix[3][2]); - tmp[2][3] = (mat.m_matrix[2][0] * m_matrix[0][3] + mat.m_matrix[2][1] * m_matrix[1][3] - + mat.m_matrix[2][2] * m_matrix[2][3] + mat.m_matrix[2][3] * m_matrix[3][3]); - - tmp[3][0] = (mat.m_matrix[3][0] * m_matrix[0][0] + mat.m_matrix[3][1] * m_matrix[1][0] - + mat.m_matrix[3][2] * m_matrix[2][0] + mat.m_matrix[3][3] * m_matrix[3][0]); - tmp[3][1] = (mat.m_matrix[3][0] * m_matrix[0][1] + mat.m_matrix[3][1] * m_matrix[1][1] - + mat.m_matrix[3][2] * m_matrix[2][1] + mat.m_matrix[3][3] * m_matrix[3][1]); - tmp[3][2] = (mat.m_matrix[3][0] * m_matrix[0][2] + mat.m_matrix[3][1] * m_matrix[1][2] - + mat.m_matrix[3][2] * m_matrix[2][2] + mat.m_matrix[3][3] * m_matrix[3][2]); - tmp[3][3] = (mat.m_matrix[3][0] * m_matrix[0][3] + mat.m_matrix[3][1] * m_matrix[1][3] - + mat.m_matrix[3][2] * m_matrix[2][3] + mat.m_matrix[3][3] * m_matrix[3][3]); - - setMatrix(tmp); - return *this; - } - - void TransformationMatrix::multVecMatrix(double x, double y, double& resultX, double& resultY) const - { - resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0]; - resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1]; - double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3]; - if (w != 1 && w != 0) { - resultX /= w; - resultY /= w; - } - } - - void TransformationMatrix::multVecMatrix(double x, double y, double z, double& resultX, double& resultY, double& resultZ) const - { - resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0] + z * m_matrix[2][0]; - resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1] + z * m_matrix[2][1]; - resultZ = m_matrix[3][2] + x * m_matrix[0][2] + y * m_matrix[1][2] + z * m_matrix[2][2]; - double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3] + z * m_matrix[2][3]; - if (w != 1 && w != 0) { - resultX /= w; - resultY /= w; - resultZ /= w; - } - } - - bool TransformationMatrix::isInvertible() const - { - if (isIdentityOrTranslation()) - return true; - - double det = WebCore::determinant4x4(m_matrix); - - if (fabs(det) < SMALL_NUMBER) - return false; - - return true; - } - - TransformationMatrix TransformationMatrix::inverse() const - { - if (isIdentityOrTranslation()) { - // identity matrix - if (m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0) - return TransformationMatrix(); - - // translation - return TransformationMatrix(1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - -m_matrix[3][0], -m_matrix[3][1], -m_matrix[3][2], 1); - } - - TransformationMatrix invMat; - bool inverted = WebCore::inverse(m_matrix, invMat.m_matrix); - if (!inverted) - return TransformationMatrix(); - - return invMat; - } - - void TransformationMatrix::makeAffine() - { - m_matrix[0][2] = 0; - m_matrix[0][3] = 0; - - m_matrix[1][2] = 0; - m_matrix[1][3] = 0; - - m_matrix[2][0] = 0; - m_matrix[2][1] = 0; - m_matrix[2][2] = 1; - m_matrix[2][3] = 0; - - m_matrix[3][2] = 0; - m_matrix[3][3] = 1; - } - - static inline void blendFloat(double& from, double to, double progress) - { - if (from != to) - from = from + (to - from) * progress; - } - - void TransformationMatrix::blend(const TransformationMatrix& from, double progress) - { - if (from.isIdentity() && isIdentity()) - return; - - // decompose - DecomposedType fromDecomp; - DecomposedType toDecomp; - from.decompose(fromDecomp); - decompose(toDecomp); - - // interpolate - blendFloat(fromDecomp.scaleX, toDecomp.scaleX, progress); - blendFloat(fromDecomp.scaleY, toDecomp.scaleY, progress); - blendFloat(fromDecomp.scaleZ, toDecomp.scaleZ, progress); - blendFloat(fromDecomp.skewXY, toDecomp.skewXY, progress); - blendFloat(fromDecomp.skewXZ, toDecomp.skewXZ, progress); - blendFloat(fromDecomp.skewYZ, toDecomp.skewYZ, progress); - blendFloat(fromDecomp.translateX, toDecomp.translateX, progress); - blendFloat(fromDecomp.translateY, toDecomp.translateY, progress); - blendFloat(fromDecomp.translateZ, toDecomp.translateZ, progress); - blendFloat(fromDecomp.perspectiveX, toDecomp.perspectiveX, progress); - blendFloat(fromDecomp.perspectiveY, toDecomp.perspectiveY, progress); - blendFloat(fromDecomp.perspectiveZ, toDecomp.perspectiveZ, progress); - blendFloat(fromDecomp.perspectiveW, toDecomp.perspectiveW, progress); - - slerp(&fromDecomp.quaternionX, &toDecomp.quaternionX, progress); - - // recompose - recompose(fromDecomp); - } - - bool TransformationMatrix::decompose(DecomposedType& decomp) const - { - if (isIdentity()) { - memset(&decomp, 0, sizeof(decomp)); - decomp.perspectiveW = 1; - decomp.scaleX = 1; - decomp.scaleY = 1; - decomp.scaleZ = 1; - } - - if (!WebCore::decompose(m_matrix, decomp)) - return false; - return true; - } - - void TransformationMatrix::recompose(const DecomposedType& decomp, bool useEulerAngle) - { - makeIdentity(); - - // first apply perspective - m_matrix[0][3] = decomp.perspectiveX; - m_matrix[1][3] = decomp.perspectiveY; - m_matrix[2][3] = decomp.perspectiveZ; - m_matrix[3][3] = decomp.perspectiveW; - - // now translate - translate3d(decomp.translateX, decomp.translateY, decomp.translateZ); - - if (!useEulerAngle) { - // apply rotation - double xx = decomp.quaternionX * decomp.quaternionX; - double xy = decomp.quaternionX * decomp.quaternionY; - double xz = decomp.quaternionX * decomp.quaternionZ; - double xw = decomp.quaternionX * decomp.quaternionW; - double yy = decomp.quaternionY * decomp.quaternionY; - double yz = decomp.quaternionY * decomp.quaternionZ; - double yw = decomp.quaternionY * decomp.quaternionW; - double zz = decomp.quaternionZ * decomp.quaternionZ; - double zw = decomp.quaternionZ * decomp.quaternionW; - - // Construct a composite rotation matrix from the quaternion values - TransformationMatrix rotationMatrix(1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw), 0, - 2 * (xy + zw), 1 - 2 * (xx + zz), 2 * (yz - xw), 0, - 2 * (xz - yw), 2 * (yz + xw), 1 - 2 * (xx + yy), 0, - 0, 0, 0, 1); - - multiply(rotationMatrix); - } else { - rotate3d(1.0, 0.0, 0.0, rad2deg(decomp.rotateX)); - rotate3d(0.0, 1.0, 0.0, rad2deg(decomp.rotateY)); - rotate3d(0.0, 0.0, 1.0, rad2deg(decomp.rotateZ)); - } - - // now apply skew - if (decomp.skewYZ) { - TransformationMatrix tmp; - tmp.setM32(decomp.skewYZ); - multiply(tmp); - } - - if (decomp.skewXZ) { - TransformationMatrix tmp; - tmp.setM31(decomp.skewXZ); - multiply(tmp); - } - - if (decomp.skewXY) { - TransformationMatrix tmp; - tmp.setM21(decomp.skewXY); - multiply(tmp); - } - - // finally, apply scale - scale3d(decomp.scaleX, decomp.scaleY, decomp.scaleZ); - } -} \ No newline at end of file diff --git a/Pods/pop/pop/WebCore/TransformationMatrix.h b/Pods/pop/pop/WebCore/TransformationMatrix.h deleted file mode 100644 index b99ae89..0000000 --- a/Pods/pop/pop/WebCore/TransformationMatrix.h +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TransformationMatrix_h -#define TransformationMatrix_h - -#include //for memcpy - -#include - -#include - -namespace WebCore { - - class TransformationMatrix { - public: - - typedef double Matrix4[4][4]; - - TransformationMatrix() { makeIdentity(); } - TransformationMatrix(const TransformationMatrix& t) { *this = t; } - TransformationMatrix(double a, double b, double c, double d, double e, double f) { setMatrix(a, b, c, d, e, f); } - TransformationMatrix(double m11, double m12, double m13, double m14, - double m21, double m22, double m23, double m24, - double m31, double m32, double m33, double m34, - double m41, double m42, double m43, double m44) - { - setMatrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44); - } - - void setMatrix(double a, double b, double c, double d, double e, double f) - { - m_matrix[0][0] = a; m_matrix[0][1] = b; m_matrix[0][2] = 0; m_matrix[0][3] = 0; - m_matrix[1][0] = c; m_matrix[1][1] = d; m_matrix[1][2] = 0; m_matrix[1][3] = 0; - m_matrix[2][0] = 0; m_matrix[2][1] = 0; m_matrix[2][2] = 1; m_matrix[2][3] = 0; - m_matrix[3][0] = e; m_matrix[3][1] = f; m_matrix[3][2] = 0; m_matrix[3][3] = 1; - } - - void setMatrix(double m11, double m12, double m13, double m14, - double m21, double m22, double m23, double m24, - double m31, double m32, double m33, double m34, - double m41, double m42, double m43, double m44) - { - m_matrix[0][0] = m11; m_matrix[0][1] = m12; m_matrix[0][2] = m13; m_matrix[0][3] = m14; - m_matrix[1][0] = m21; m_matrix[1][1] = m22; m_matrix[1][2] = m23; m_matrix[1][3] = m24; - m_matrix[2][0] = m31; m_matrix[2][1] = m32; m_matrix[2][2] = m33; m_matrix[2][3] = m34; - m_matrix[3][0] = m41; m_matrix[3][1] = m42; m_matrix[3][2] = m43; m_matrix[3][3] = m44; - } - - TransformationMatrix& operator =(const TransformationMatrix &t) - { - setMatrix(t.m_matrix); - return *this; - } - - TransformationMatrix& makeIdentity() - { - setMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); - return *this; - } - - bool isIdentity() const - { - return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0 && - m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && m_matrix[1][2] == 0 && m_matrix[1][3] == 0 && - m_matrix[2][0] == 0 && m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0 && - m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0 && m_matrix[3][3] == 1; - } - - // This form preserves the double math from input to output - void map(double x, double y, double& x2, double& y2) const { multVecMatrix(x, y, x2, y2); } - - double m11() const { return m_matrix[0][0]; } - void setM11(double f) { m_matrix[0][0] = f; } - double m12() const { return m_matrix[0][1]; } - void setM12(double f) { m_matrix[0][1] = f; } - double m13() const { return m_matrix[0][2]; } - void setM13(double f) { m_matrix[0][2] = f; } - double m14() const { return m_matrix[0][3]; } - void setM14(double f) { m_matrix[0][3] = f; } - double m21() const { return m_matrix[1][0]; } - void setM21(double f) { m_matrix[1][0] = f; } - double m22() const { return m_matrix[1][1]; } - void setM22(double f) { m_matrix[1][1] = f; } - double m23() const { return m_matrix[1][2]; } - void setM23(double f) { m_matrix[1][2] = f; } - double m24() const { return m_matrix[1][3]; } - void setM24(double f) { m_matrix[1][3] = f; } - double m31() const { return m_matrix[2][0]; } - void setM31(double f) { m_matrix[2][0] = f; } - double m32() const { return m_matrix[2][1]; } - void setM32(double f) { m_matrix[2][1] = f; } - double m33() const { return m_matrix[2][2]; } - void setM33(double f) { m_matrix[2][2] = f; } - double m34() const { return m_matrix[2][3]; } - void setM34(double f) { m_matrix[2][3] = f; } - double m41() const { return m_matrix[3][0]; } - void setM41(double f) { m_matrix[3][0] = f; } - double m42() const { return m_matrix[3][1]; } - void setM42(double f) { m_matrix[3][1] = f; } - double m43() const { return m_matrix[3][2]; } - void setM43(double f) { m_matrix[3][2] = f; } - double m44() const { return m_matrix[3][3]; } - void setM44(double f) { m_matrix[3][3] = f; } - - double a() const { return m_matrix[0][0]; } - void setA(double a) { m_matrix[0][0] = a; } - - double b() const { return m_matrix[0][1]; } - void setB(double b) { m_matrix[0][1] = b; } - - double c() const { return m_matrix[1][0]; } - void setC(double c) { m_matrix[1][0] = c; } - - double d() const { return m_matrix[1][1]; } - void setD(double d) { m_matrix[1][1] = d; } - - double e() const { return m_matrix[3][0]; } - void setE(double e) { m_matrix[3][0] = e; } - - double f() const { return m_matrix[3][1]; } - void setF(double f) { m_matrix[3][1] = f; } - - // this = this * mat - TransformationMatrix& multiply(const TransformationMatrix&); - - TransformationMatrix& scale(double); - TransformationMatrix& scaleNonUniform(double sx, double sy); - TransformationMatrix& scale3d(double sx, double sy, double sz); - - TransformationMatrix& rotate(double d) { return rotate3d(0, 0, d); } - TransformationMatrix& rotateFromVector(double x, double y); - TransformationMatrix& rotate3d(double rx, double ry, double rz); - - // The vector (x,y,z) is normalized if it's not already. A vector of - // (0,0,0) uses a vector of (0,0,1). - TransformationMatrix& rotate3d(double x, double y, double z, double angle); - - TransformationMatrix& translate(double tx, double ty); - TransformationMatrix& translate3d(double tx, double ty, double tz); - - // translation added with a post-multiply - TransformationMatrix& translateRight(double tx, double ty); - TransformationMatrix& translateRight3d(double tx, double ty, double tz); - - TransformationMatrix& flipX(); - TransformationMatrix& flipY(); - TransformationMatrix& skew(double angleX, double angleY); - TransformationMatrix& skewX(double angle) { return skew(angle, 0); } - TransformationMatrix& skewY(double angle) { return skew(0, angle); } - - TransformationMatrix& applyPerspective(double p); - bool hasPerspective() const { return m_matrix[2][3] != 0.0f; } - - bool isInvertible() const; - - // This method returns the identity matrix if it is not invertible. - // Use isInvertible() before calling this if you need to know. - TransformationMatrix inverse() const; - - // decompose the matrix into its component parts - typedef struct { - double scaleX, scaleY, scaleZ; - double skewXY, skewXZ, skewYZ; - double rotateX, rotateY, rotateZ; - double quaternionX, quaternionY, quaternionZ, quaternionW; - double translateX, translateY, translateZ; - double perspectiveX, perspectiveY, perspectiveZ, perspectiveW; - } DecomposedType; - - bool decompose(DecomposedType& decomp) const; - void recompose(const DecomposedType& decomp, bool useEulerAngle = false); - - void blend(const TransformationMatrix& from, double progress); - - bool isAffine() const - { - return (m13() == 0 && m14() == 0 && m23() == 0 && m24() == 0 && - m31() == 0 && m32() == 0 && m33() == 1 && m34() == 0 && m43() == 0 && m44() == 1); - } - - // Throw away the non-affine parts of the matrix (lossy!) - void makeAffine(); - - bool operator==(const TransformationMatrix& m2) const - { - return (m_matrix[0][0] == m2.m_matrix[0][0] && - m_matrix[0][1] == m2.m_matrix[0][1] && - m_matrix[0][2] == m2.m_matrix[0][2] && - m_matrix[0][3] == m2.m_matrix[0][3] && - m_matrix[1][0] == m2.m_matrix[1][0] && - m_matrix[1][1] == m2.m_matrix[1][1] && - m_matrix[1][2] == m2.m_matrix[1][2] && - m_matrix[1][3] == m2.m_matrix[1][3] && - m_matrix[2][0] == m2.m_matrix[2][0] && - m_matrix[2][1] == m2.m_matrix[2][1] && - m_matrix[2][2] == m2.m_matrix[2][2] && - m_matrix[2][3] == m2.m_matrix[2][3] && - m_matrix[3][0] == m2.m_matrix[3][0] && - m_matrix[3][1] == m2.m_matrix[3][1] && - m_matrix[3][2] == m2.m_matrix[3][2] && - m_matrix[3][3] == m2.m_matrix[3][3]); - } - - bool operator!=(const TransformationMatrix& other) const { return !(*this == other); } - - // *this = *this * t (i.e., a multRight) - TransformationMatrix& operator*=(const TransformationMatrix& t) - { - return multiply(t); - } - - // result = *this * t (i.e., a multRight) - TransformationMatrix operator*(const TransformationMatrix& t) - { - TransformationMatrix result = *this; - result.multiply(t); - return result; - } - - CATransform3D transform3d () const; - CGAffineTransform affineTransform () const; - - TransformationMatrix(const CATransform3D&); - operator CATransform3D() const; - - TransformationMatrix(const CGAffineTransform&); - operator CGAffineTransform() const; - - private: - - // multiply passed 2D point by matrix (assume z=0) - void multVecMatrix(double x, double y, double& dstX, double& dstY) const; - - // multiply passed 3D point by matrix - void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const; - - void setMatrix(const Matrix4 m) - { - if (m && m != m_matrix) - memcpy(m_matrix, m, sizeof(Matrix4)); - } - - bool isIdentityOrTranslation() const - { - return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0 && - m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && m_matrix[1][2] == 0 && m_matrix[1][3] == 0 && - m_matrix[2][0] == 0 && m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0 && - m_matrix[3][3] == 1; - } - - Matrix4 m_matrix; - }; - -} // namespace WebCore - -#endif // TransformationMatrix_h diff --git a/Pods/pop/pop/WebCore/UnitBezier.h b/Pods/pop/pop/WebCore/UnitBezier.h deleted file mode 100644 index 0f847a0..0000000 --- a/Pods/pop/pop/WebCore/UnitBezier.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef UnitBezier_h -#define UnitBezier_h - -#include - -namespace WebCore { - - struct UnitBezier { - UnitBezier(double p1x, double p1y, double p2x, double p2y) - { - // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). - cx = 3.0 * p1x; - bx = 3.0 * (p2x - p1x) - cx; - ax = 1.0 - cx -bx; - - cy = 3.0 * p1y; - by = 3.0 * (p2y - p1y) - cy; - ay = 1.0 - cy - by; - } - - double sampleCurveX(double t) - { - // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. - return ((ax * t + bx) * t + cx) * t; - } - - double sampleCurveY(double t) - { - return ((ay * t + by) * t + cy) * t; - } - - double sampleCurveDerivativeX(double t) - { - return (3.0 * ax * t + 2.0 * bx) * t + cx; - } - - // Given an x value, find a parametric value it came from. - double solveCurveX(double x, double epsilon) - { - double t0; - double t1; - double t2; - double x2; - double d2; - int i; - - // First try a few iterations of Newton's method -- normally very fast. - for (t2 = x, i = 0; i < 8; i++) { - x2 = sampleCurveX(t2) - x; - if (fabs (x2) < epsilon) - return t2; - d2 = sampleCurveDerivativeX(t2); - if (fabs(d2) < 1e-6) - break; - t2 = t2 - x2 / d2; - } - - // Fall back to the bisection method for reliability. - t0 = 0.0; - t1 = 1.0; - t2 = x; - - if (t2 < t0) - return t0; - if (t2 > t1) - return t1; - - while (t0 < t1) { - x2 = sampleCurveX(t2); - if (fabs(x2 - x) < epsilon) - return t2; - if (x > x2) - t0 = t2; - else - t1 = t2; - t2 = (t1 - t0) * .5 + t0; - } - - // Failure. - return t2; - } - - double solve(double x, double epsilon) - { - return sampleCurveY(solveCurveX(x, epsilon)); - } - - private: - double ax; - double bx; - double cx; - - double ay; - double by; - double cy; - }; -} -#endif diff --git a/README.md b/README.md index 972e4c9..4beca2f 100644 --- a/README.md +++ b/README.md @@ -1,117 +1,191 @@ +# 《大厂面试指北》 + +最佳阅读地址:http://notfound9.github.io/interviewGuide/ + +Github项目主页:https://github.com/NotFound9/interviewGuide + +作者博客地址:https://juejin.im/user/5b370a42e51d4558ce5eb969 + +## 为什么要做这个开源项目? + +之前在业余时间阅读技术书籍时,发现只阅读而不产出,这样收效甚微。所以就在网上找了很多常见的技术问题,根据自己的技术积累,查阅书籍,阅读文档和博客等资料,尝试着用自己的话去写了很多原创解答,最终整理开源到Github。一方面是便于自己复习巩固,一方面是将这些自己写的解答开源出来分享给大家,希望可以帮助到大家,也欢迎大家一起来完善这个项目,为开源做贡献。 + +
+ + + +## 目录 + +- [首页](README.md) +* Java + - [基础](docs/JavaBasic.md) + - [1.Java中的多态是什么?](docs/JavaBasic.md#Java中的多态是什么?) + - [2.Java中变量,代码块,构造器之间执行顺序是怎么样的?](docs/JavaBasic.md#java中变量,代码块,构造器之间执行顺序是怎么样的?) + - [3.final关键字有哪些作用?](docs/JavaBasic.md#final关键字有哪些作用?) + - [4.Integer类会进行缓存吗?](docs/JavaBasic.md#Integer类会进行缓存吗?) + - [5.抽象类有哪些特点?](docs/JavaBasic.md#抽象类有哪些特点?) + - [6.String,StringBuffer和StringBuilder之间的区别是什么?](docs/JavaBasic.md#String,StringBuffer和StringBuilder之间的区别是什么?) + - [7.编译型编程语言,解释型编程语言,伪编译型语言的区别是什么?](docs/JavaBasic.md#编译型编程语言,解释型编程语言,伪编译型语言的区别是什么?) + - [8.Java中的访问控制符有哪些?](docs/JavaBasic.md#Java中的访问控制符有哪些?) + - [9.Java的构造器有哪些特点?](docs/JavaBasic.md#Java的构造器有哪些特点?) + - [10.Java中的内部类是怎么样的?](docs/JavaBasic.md#Java中的内部类是怎么样的?) + - [11.Java中的注解是什么?](docs/JavaBasic.md#Java中的注解是什么?) + - [12.为什么hashCode()和equal()方法要一起重写?](docs/JavaBasic.md#为什么hashCode()和equal()方法要一起重写?) + - [13.Java中有哪些数据类型?](docs/JavaBasic.md#Java中有哪些数据类型?) + - [14.包装类型和基本类型的区别是什么?](docs/JavaBasic.md#包装类型和基本类型的区别是什么?) + * 容器 + - [ArrayList和LinkedList](docs/ArrayList.md) + - [1.ArrayList与LinkedList的区别是什么?](docs/ArrayList.md#ArrayList与LinkedList的区别是什么?) + - [2.怎么使ArrayList,LinkedList变成线程安全的呢?](docs/ArrayList.md#怎么使ArrayList,LinkedList变成线程安全的呢?) + - [3.ArrayList遍历时删除元素有哪些方法?](docs/ArrayList.md#ArrayList遍历时删除元素有哪些方法?) + - [4.ConcurrentModificationException是什么?](docs/ArrayList.md#ConcurrentModificationException是什么?) + - [5.java容器类的层次是怎么样的?](docs/ArrayList.md#java容器类的层次是怎么样的?) + - [HashMap和ConcurrentHashMap](docs/HashMap.md) + - [1.HashMap添加一个键值对的过程是怎么样的?](docs/HashMap.md#HashMap添加一个键值对的过程是怎么样的?) + - [2.ConcurrentHashMap添加一个键值对的过程是怎么样的?](docs/HashMap.md#ConcurrentHashMap添加一个键值对的过程是怎么样的?) + - [3.HashMap与HashTable,ConcurrentHashMap的区别是什么?](docs/HashMap.md#HashMap与HashTable,ConcurrentHashMap的区别是什么?) + - [4.HashMap扩容后是否需要rehash?](docs/HashMap.md#HashMap扩容后是否需要rehash?) + - [5.HashMap扩容是怎样扩容的,为什么都是2的N次幂的大小?](docs/HashMap.md#HashMap扩容是怎样扩容的,为什么都是2的N次幂的大小?) + - [6.ConcurrentHashMap是怎么记录元素个数size的?](docs/HashMap.md#ConcurrentHashMap是怎么记录元素个数size的?) + - [7.为什么ConcurrentHashMap,HashTable不支持key,value为null?](docs/HashMap.md#为什么ConcurrentHashMap,HashTable不支持key,value为null?) + - [8.HashSet和HashMap的区别?](docs/HashMap.md#HashSet和HashMap的区别?) + - [9.HashMap遍历时删除元素的有哪些实现方法?](docs/HashMap.md#HashMap遍历时删除元素的有哪些实现方法?) + - [多线程](docs/JavaMultiThread.md) + - [1.进程与线程的区别是什么?](docs/JavaMultiThread.md#进程与线程的区别是什么?) + - [2.进程间如何通信?](docs/JavaMultiThread.md#进程间如何通信?) + - [3.Java中单例有哪些写法?](docs/JavaMultiThread.md#Java中单例有哪些写法?) + - [4.Java中创建线程有哪些方式?](docs/JavaMultiThread.md#Java中创建线程有哪些方式?) + - [5.如何解决序列化时可以创建出单例对象的问题?](docs/JavaMultiThread.md#如何解决序列化时可以创建出单例对象的问题?) + - [6.volatile关键字有什么用?怎么理解可见性,一般什么场景去用可见性?](docs/JavaMultiThread.md#volatile关键字有什么用?怎么理解可见性,一般什么场景去用可见性?) + - [7.Java中线程的状态是怎么样的?](docs/JavaMultiThread.md#Java中线程的状态是怎么样的?) + - [8.wait(),join(),sleep()方法有什么作用?](docs/JavaMultiThread.md#wait(),join(),sleep()方法有什么作用?) + - [9.Thread.sleep(),Object.wait(),LockSupport.park()有什么区别?](docs/JavaMultiThread.md#Thread.sleep(),Object.wait(),LockSupport.park()有什么区别?) + - [10.谈一谈你对线程中断的理解?](docs/JavaMultiThread.md#谈一谈你对线程中断的理解?) + - [11.线程间怎么通信?](docs/JavaMultiThread.md#线程间怎么通信?) + - [12.怎么实现实现一个生产者消费者?](docs/JavaMultiThread.md#怎么实现实现一个生产者消费者?) + - [13.谈一谈你对线程池的理解?](docs/JavaMultiThread.md#谈一谈你对线程池的理解?) + - [14.线程池有哪些状态?](docs/JavaMultiThread.md#线程池有哪些状态?) + - [锁相关](docs/Lock.md) + - [1.sychronize的实现原理是怎么样的?](docs/Lock.md#sychronize的实现原理是怎么样的?) + - [2.AbstractQueuedSynchronizer(缩写为AQS)是什么?](docs/Lock.md#AbstractQueuedSynchronizer(缩写为AQS)是什么?) + - [3.悲观锁和乐观锁是什么?](docs/Lock.md#悲观锁和乐观锁是什么?) +* Redis + - [基础](docs/RedisBasic.md) + - [1.Redis是什么?](docs/RedisBasic.md#Redis是什么?) + - [2.Redis过期key是怎么样清理的?](docs/RedisBasic.md#Redis过期key是怎么样清理的?) + - [3.Redis为什么是单线程的?](docs/RedisBasic.md#Redis为什么是单线程的?) + - [4.Redis的性能为什么这么高?](docs/RedisBasic.md#Redis的性能为什么这么高?) + - [5.Linux中IO模型一共有哪些?](docs/RedisBasic.md#Linux中IO模型一共有哪些?) + - [6.同步与异步的区别是什么?](docs/RedisBasic.md#同步与异步的区别是什么?) + - [7.阻塞与非阻塞的区别是什么?](docs/RedisBasic.md#阻塞与非阻塞的区别是什么?) + - [8.如何解决Redis缓存穿透问题?](docs/RedisBasic.md#如何解决Redis缓存穿透问题?) + - [9.如何解决Redis缓存击穿问题?](docs/RedisBasic.md#如何解决Redis缓存击穿问题?) + - [10.如何解决Redis缓存雪崩问题?](docs/RedisBasic.md#如何解决Redis缓存雪崩问题?) + - [11.如何解决缓存与数据库的数据一致性问题?](docs/RedisBasic.md#如何解决缓存与数据库的数据一致性问题?) + - [数据结构](docs/RedisDataStruct.md) + - [1.Redis常见的数据结构有哪些?](docs/RedisDataStruct.md#Redis常见的数据结构有哪些?) + - [2.谈一谈你对Redis中简单动态字符串的理解?](docs/RedisDataStruct.md#谈一谈你对Redis中简单动态字符串的理解?) + - [3.谈一谈你对Redis中hash对象的理解?](docs/RedisDataStruct.md#谈一谈你对Redis中hash对象的理解?) + - [4.谈一谈你对Redis中List的理解?](docs/RedisDataStruct.md#谈一谈你对Redis中List的理解?) + - [5.谈一谈你对Redis中Set的理解?](docs/RedisDataStruct.md#谈一谈你对Redis中Set的理解?) + - [6.谈一谈你对Redis中有序集合ZSet的理解?](docs/RedisDataStruct.md#谈一谈你对Redis中有序集合ZSet的理解?) + - [7.布隆过滤器是什么?](docs/RedisDataStruct.md#布隆过滤器是什么?) + - [持久化(AOF和RDB)](docs/RedisStore.md) + - [1.Redis的持久化是怎么实现的?](docs/RedisStore.md#Redis的持久化是怎么实现的?) + - [2.AOF和RDB的区别是什么?](docs/RedisStore.md#AOF和RDB的区别是什么?) + - [3.怎么防止AOF文件越来越大?](docs/RedisStore.md#怎么防止AOF文件越来越大?) + - [4.Redis持久化策略该如何进行选择?](docs/RedisStore.md#Redis持久化策略该如何进行选择?) + - [高可用(主从切换和哨兵机制)](docs/RedisUserful.md) + - [1.Redis主从同步是怎么实现的?](docs/RedisUserful.md#Redis主从同步是怎么实现的?) + - [2.Redis中哨兵是什么?](docs/RedisUserful.md#Redis中哨兵是什么?) + - [3.客户端是怎么接入哨兵系统的?](docs/RedisUserful.md#客户端是怎么接入哨兵系统的?) + - [4.Redis哨兵系统是怎么实现自动故障转移的?](docs/RedisUserful.md#Redis哨兵系统是怎么实现自动故障转移的?) + - [5.谈一谈你对Redis Cluster的理解?](docs/RedisUserful.md#谈一谈你对RedisCluster的理解?) + - [6.RedisCluster是怎么实现数据分片的?](docs/RedisUserful.md#RedisCluster是怎么实现数据分片的?) + - [7.RedisCluster是怎么做故障转移和发现的?](docs/RedisUserful.md#RedisCluster是怎么做故障转移和发现的?) +* MySQL + - [基础](docs/MySQLNote.md) + - [1.一条MySQL更新语句的执行过程是什么样的?](docs/MySQLNote.md#一条MySQL更新语句的执行过程是什么样的?) + - [2.脏页是什么?](docs/MySQLNote.md#脏页是什么?) + - [3.Checkpoint是什么?](docs/MySQLNote.md#Checkpoint是什么?) + - [4.undo log,redo log,bin log是什么?](docs/MySQLNote.md#undolog,redolog,binlog是什么?) + - [5.MySQL中的事务是什么?](docs/MySQLNote.md#MySQL中的事务是什么?) + - [6.MySQL的隔离级别是怎么样的?](docs/MySQLNote.md#MySQL的隔离级别是怎么样的?) + - [7.MVCC的实现原理是怎么样的?](docs/MySQLNote.md#MVCC的实现原理是怎么样的?) + - [8.MySQL是怎么解决幻读的问题的?](docs/MySQLNote.md#MySQL是怎么解决幻读的问题的?) + - [9.MySQL中有哪些锁?](docs/MySQLNote.md#MySQL中有哪些锁?) + - [10.B树是什么?](docs/MySQLNote.md#B树是什么?) + - [11.B树与B+树的区别是什么?](docs/MySQLNote.md#B树与B+树的区别是什么?) + - [12.索引是什么?](docs/MySQLNote.md#索引是什么?) + - [13.字符串索引和数字类型索引的区别?](docs/MySQLNote.md#字符串索引和数字类型索引的区别?) + - [14.union和union all的区别是什么?](docs/MySQLNote.md#union和union) + - [15.Join的工作流程是怎么样的,怎么进行优化?](docs/MySQLNote.md#Join的工作流程是怎么样的,怎么进行优化) + - [16.聚集索引是什么?](docs/MySQLNote.md#聚集索引是什么?) + - [17.联合索引是什么?](docs/MySQLNote.md#联合索引是什么?) + - [18.覆盖索引是什么?](docs/MySQLNote.md#覆盖索引是什么?) + - [19.哪些情况不要建索引?](docs/MySQLNote.md#哪些情况不要建索引?) + - [20.主键,唯一性索引,普通索引的区别是什么?](docs/MySQLNote.md#主键,唯一性索引,普通索引的区别是什么?) + - [21.InnoDB和MyISAM的区别是什么?](docs/MySQLNote.md#InnoDB和MyISAM的区别是什么?) + - [22.什么是分库分表?](docs/MySQLNote.md#什么是分库分表?) + - [23.怎么实现跨库分页查询?](docs/MySQLNote.md#怎么实现跨库分页查询?) + - [24.MySQL主从复制的工作流程是什么样的?](docs/MySQLNote.md#MySQL主从复制的工作流程是什么样的?) + - [25.char类型与varchar类型的区别?](docs/MySQLNote.md#char类型与varchar类型的区别?) + - [26.查询数量SELECT Count(*)怎么优化?](docs/MySQLNote.md#怎么优化数量查询?) + - [27.如何优化MySQL慢查询?](docs/MySQLNote.md#如何优化MySQL慢查询?) + - [28.MySQL的join的实现是怎么样的?](docs/MySQLNote.md#MySQL的join的实现是怎么样的?) + - [慢查询优化实践](docs/MySQLWork.md) +* JVM + - [基础](docs/JavaJVM.md) + - [1.Java内存区域怎么划分的?](docs/JavaJVM.md#Java内存区域怎么划分的?) + - [2.Java中对象的创建过程是怎么样的?](docs/JavaJVM.md#Java中对象的创建过程是怎么样的?) + - [3.Java对象的内存布局是怎么样的?](docs/JavaJVM.md#Java对象的内存布局是怎么样的?) + - [4.垃圾回收有哪些特点?](docs/JavaJVM.md#垃圾回收有哪些特点?) + - [5.在垃圾回收机制中,对象在内存中的状态有哪几种?](docs/JavaJVM.md#在垃圾回收机制中,对象在内存中的状态有哪几种?) + - [6.对象的强引用,软引用,弱引用和虚引用的区别是什么?](docs/JavaJVM.md#对象的强引用,软引用,弱引用和虚引用的区别是什么?) + - [7.双亲委派机制是什么?](docs/JavaJVM.md#双亲委派机制是什么?) + - [8.怎么自定义一个类加载器?](docs/JavaJVM.md#怎么自定义一个类加载器?) + - [9.垃圾回收算法有哪些?](docs/JavaJVM.md#垃圾回收算法有哪些?) + - [10.Minor GC和Full GC是什么?](docs/JavaJVM.md#MinorGC和FullGC是什么?) + - [11.如何确定一个对象可以回收?](docs/JavaJVM.md#如何确定一个对象是否可以被回收?) + - [12.目前通常使用的是什么垃圾收集器?](docs/JavaJVM.md#目前通常使用的是什么垃圾收集器?) +- [Kafka](docs/Kafka.md) +- [ZooKeeper](docs/ZooKeeper.md) +- [HTTP](docs/HTTP.md) +- [Spring](docs/Spring.md) +- [Nginx](docs/Nginx.md) +- [系统设计](docs/SystemDesign.md) +* 算法 + - [《剑指Offer》解题思考](docs/CodingInterviews.md) + - [《LeetCode热门100题》解题思考(上)](docs/LeetCode.md) + - [《LeetCode热门100题》解题思考(下)](docs/LeetCode1.md) +- [大厂面试公众号文章系列](docs/BATInterview.md) + - [【大厂面试01期】高并发场景下,如何保证缓存与数据库一致性?](https://mp.weixin.qq.com/s/hwMpAVZ1_p8gLfPAzA8X9w) + - [【大厂面试02期】Redis过期key是怎么样清理的?](https://mp.weixin.qq.com/s/J_nOPKS17Uax2zGrZsE8ZA) + - [【大厂面试03期】MySQL是怎么解决幻读问题的?](https://mp.weixin.qq.com/s/8D6EmZM3m6RiSk0-N5YCww) + - [【大厂面试04期】讲讲一条MySQL更新语句是怎么执行的?](https://mp.weixin.qq.com/s/pNe1vdTT24oEoJS_zs-5jQ) + - [【大厂面试05期】说一说你对MySQL中锁的理解?](https://mp.weixin.qq.com/s/pTpPE33X-iYULYt8DOPp2w) + - [【大厂面试06期】谈一谈你对Redis持久化的理解?](https://mp.weixin.qq.com/s/nff4fd5TnM-CMWb1hQIT9Q) + - [【大厂面试07期】说一说你对synchronized锁的理解?](https://mp.weixin.qq.com/s/H8Cd2fj82qbdLZKBlo-6Dg) + - [【大厂面试08期】谈一谈你对HashMap的理解?](https://mp.weixin.qq.com/s/b4f5NIPl9uVLkRg_UpWSJQ) +* 读书笔记 + - [《Redis设计与实现》读书笔记 上](docs/RedisBook1.md) + - [《Redis设计与实现》读书笔记 下](docs/RedisBook2.md) + - [《MySQL必知必会》读书笔记](docs/MySQLBook1.md) + - [《深入理解Java虚拟机-第三版》读书笔记](docs/JVMBook.md) +- [好书推荐](docs/bookRecommend.md) + +## 如何为这个开源项目做贡献? + +如果你想一起参与这个项目,可以提Pull Request,可以扫上面的入群二维码进群,如果入群二维码失效了,也可以扫我的微信,我们一起聊聊! + +## 关于我 + +我平时比较喜欢看书,写技术文章,也比较喜欢讨论技术。这是我的[掘金主页](https://juejin.im/user/5b370a42e51d4558ce5eb969),希望大家可以关注一下,谢谢了!大家如果有事需要联系我,或者想进技术群,一起讨论技术,也可以扫描[主页中我的微信二维码](http://notfound9.github.io/interviewGuide/#/)加我,谢谢了! + + + +## 关于转载 + +如果你需要转载本仓库的一些文章到自己的博客的话,记得注明原文地址就可以了。 - -# TTNews - - -![image](https://github.com/577528249/TTNews/blob/master/introductionimages/1234.gif) - - -作为一个集新闻与娱乐于一体的新闻客户端, - - -她具有阅读新闻,观赏搞笑图片,观赏搞笑视频等功能, - - -她采用MVC架构,拥有新闻,图片,视频,我四个模块, - - -她所涉及的技术有网络请求,视频播放,数据持久化,陀螺仪监测等, - - -如果您认为她对您有所帮助,希望您能为她点一下star,感谢您的访问! - - -PS:她的新闻数据源来自于百度API Store里的免费新闻api, - -轮播图数据接口http://apistore.baidu.com/apiworks/servicedetail/1570.html - - -新闻接口http://apistore.baidu.com/apiworks/servicedetail/688.html - - - -她的搞笑图片,搞笑视频数据源来自于百思不得姐 -(api由抓包分析网络请求所得,版权归百思不得姐所有), - - -# 使用方法 - - -点击右上角Download Zip按钮,将项目压缩包下载至本地,解压后点击文件夹中TTNews.xcworkspace即可运行。 - - - - - -# 新闻首页 - - -![image](https://github.com/577528249/TTNews/blob/master/introductionimages/IMG_0345.PNG) - - - - - - - - - - - - - - - - -# 图片界面 - - -![image](https://github.com/577528249/TTNews/blob/master/introductionimages/IMG_0346.PNG) - - - - - - - - - - - - - - - -# 视频界面 - - -![image](https://github.com/577528249/TTNews/blob/master/introductionimages/IMG_0347.PNG) - - - - - - - - - - - -# 我的界面 - -![image](https://github.com/577528249/TTNews/blob/master/introductionimages/IMG_0349.PNG) - - - - -# License -The MIT License (MIT) - -Copyright (c) 2016 YangJunhui - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/TTNews.xcodeproj/project.pbxproj b/TTNews.xcodeproj/project.pbxproj deleted file mode 100644 index 1cd4556..0000000 --- a/TTNews.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1210 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 9D9B34292F23760DBD87E9B4 /* libPods-TTNews.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 59D897E651F8649ACCCD036B /* libPods-TTNews.a */; }; - EE3A071E1CDA1DAD0024C37A /* ContentTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE3A071D1CDA1DAD0024C37A /* ContentTableViewController.m */; }; - EE4635C81CC72B8900DB248F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = EE4635C71CC72B8900DB248F /* main.m */; }; - EE4635D31CC72B8900DB248F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EE4635D21CC72B8900DB248F /* Assets.xcassets */; }; - EE4635E11CC72B8A00DB248F /* TTNewsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = EE4635E01CC72B8A00DB248F /* TTNewsTests.m */; }; - EE4635EC1CC72B8A00DB248F /* TTNewsUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = EE4635EB1CC72B8A00DB248F /* TTNewsUITests.m */; }; - EE4636B81CC7499D00DB248F /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE4636B71CC7499D00DB248F /* LaunchScreen.xib */; }; - EE4BEB5D1D5480E400B4A035 /* NewsURLs.plist in Resources */ = {isa = PBXBuildFile; fileRef = EE4BEB5C1D5480E400B4A035 /* NewsURLs.plist */; }; - EE79B10E1CC8C435001029EF /* AppInfoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0901CC8C435001029EF /* AppInfoViewController.m */; }; - EE79B10F1CC8C435001029EF /* AppInfoViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0911CC8C435001029EF /* AppInfoViewController.xib */; }; - EE79B1101CC8C435001029EF /* EditUserInfoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0931CC8C435001029EF /* EditUserInfoViewController.m */; }; - EE79B1111CC8C435001029EF /* EditUserInfoViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0941CC8C435001029EF /* EditUserInfoViewController.xib */; }; - EE79B1121CC8C435001029EF /* MeTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0961CC8C435001029EF /* MeTableViewController.m */; }; - EE79B1131CC8C435001029EF /* SendFeedbackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0981CC8C435001029EF /* SendFeedbackViewController.m */; }; - EE79B1151CC8C435001029EF /* DetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0A01CC8C435001029EF /* DetailViewController.m */; }; - EE79B1161CC8C435001029EF /* NewsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0A21CC8C435001029EF /* NewsViewController.m */; }; - EE79B1171CC8C435001029EF /* ShowMultiPictureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0A41CC8C435001029EF /* ShowMultiPictureViewController.m */; }; - EE79B1181CC8C435001029EF /* TTHeaderNews.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0A71CC8C435001029EF /* TTHeaderNews.m */; }; - EE79B1191CC8C435001029EF /* TTNormalNews.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0A91CC8C435001029EF /* TTNormalNews.m */; }; - EE79B11A1CC8C435001029EF /* ChannelCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0AC1CC8C435001029EF /* ChannelCollectionViewCell.m */; }; - EE79B11B1CC8C435001029EF /* ChannelCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0AD1CC8C435001029EF /* ChannelCollectionViewCell.xib */; }; - EE79B11C1CC8C435001029EF /* MultiPictureTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0AF1CC8C435001029EF /* MultiPictureTableViewCell.m */; }; - EE79B11D1CC8C435001029EF /* MultiPictureTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0B01CC8C435001029EF /* MultiPictureTableViewCell.xib */; }; - EE79B11E1CC8C435001029EF /* NoPictureNewsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0B21CC8C435001029EF /* NoPictureNewsTableViewCell.m */; }; - EE79B11F1CC8C435001029EF /* NoPictureNewsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0B31CC8C435001029EF /* NoPictureNewsTableViewCell.xib */; }; - EE79B1201CC8C435001029EF /* SinglePictureNewsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0B51CC8C435001029EF /* SinglePictureNewsTableViewCell.m */; }; - EE79B1211CC8C435001029EF /* SinglePictureNewsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0B61CC8C435001029EF /* SinglePictureNewsTableViewCell.xib */; }; - EE79B1221CC8C435001029EF /* Foundation+Log.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0B91CC8C435001029EF /* Foundation+Log.m */; }; - EE79B1231CC8C435001029EF /* NSDate+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0BB1CC8C435001029EF /* NSDate+Extension.m */; }; - EE79B1241CC8C435001029EF /* UIBarButtonItem+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0BD1CC8C435001029EF /* UIBarButtonItem+Extension.m */; }; - EE79B1251CC8C435001029EF /* UIImage+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0BF1CC8C435001029EF /* UIImage+Extension.m */; }; - EE79B1261CC8C435001029EF /* UIImageView+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0C11CC8C435001029EF /* UIImageView+Extension.m */; }; - EE79B1271CC8C435001029EF /* UIView+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0C31CC8C435001029EF /* UIView+Extension.m */; }; - EE79B1281CC8C435001029EF /* TTConst.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0C61CC8C435001029EF /* TTConst.m */; }; - EE79B1291CC8C435001029EF /* TTDataTool.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0C81CC8C435001029EF /* TTDataTool.m */; }; - EE79B12A1CC8C435001029EF /* TTNormalNewsFetchDataParameter.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0CA1CC8C435001029EF /* TTNormalNewsFetchDataParameter.m */; }; - EE79B12B1CC8C435001029EF /* TTPictureFetchDataParameter.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0CC1CC8C435001029EF /* TTPictureFetchDataParameter.m */; }; - EE79B12C1CC8C435001029EF /* TTVideoFetchDataParameter.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0CE1CC8C435001029EF /* TTVideoFetchDataParameter.m */; }; - EE79B12D1CC8C435001029EF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0D11CC8C435001029EF /* AppDelegate.m */; }; - EE79B12E1CC8C435001029EF /* TTNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0D31CC8C435001029EF /* TTNavigationController.m */; }; - EE79B12F1CC8C435001029EF /* TTTabBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0D51CC8C435001029EF /* TTTabBarController.m */; }; - EE79B1301CC8C435001029EF /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0D81CC8C435001029EF /* Reachability.m */; }; - EE79B1311CC8C435001029EF /* TTJudgeNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0DA1CC8C435001029EF /* TTJudgeNetworking.m */; }; - EE79B1321CC8C435001029EF /* PictureCommentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0DE1CC8C435001029EF /* PictureCommentViewController.m */; }; - EE79B1331CC8C435001029EF /* PictureCommentViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0DF1CC8C435001029EF /* PictureCommentViewController.xib */; }; - EE79B1341CC8C435001029EF /* PictureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0E11CC8C435001029EF /* PictureViewController.m */; }; - EE79B1351CC8C435001029EF /* TTPicture.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0E41CC8C435001029EF /* TTPicture.m */; }; - EE79B1361CC8C435001029EF /* TTPictureComment.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0E61CC8C435001029EF /* TTPictureComment.m */; }; - EE79B1371CC8C435001029EF /* TTPictureUser.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0E81CC8C435001029EF /* TTPictureUser.m */; }; - EE79B1381CC8C435001029EF /* PictureCommentCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0EB1CC8C435001029EF /* PictureCommentCell.m */; }; - EE79B1391CC8C435001029EF /* PictureCommentCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0EC1CC8C435001029EF /* PictureCommentCell.xib */; }; - EE79B13A1CC8C435001029EF /* PictureTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0EE1CC8C435001029EF /* PictureTableViewCell.m */; }; - EE79B13B1CC8C435001029EF /* PictureTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0EF1CC8C435001029EF /* PictureTableViewCell.xib */; }; - EE79B13C1CC8C435001029EF /* ShowBigPictureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0F11CC8C435001029EF /* ShowBigPictureViewController.m */; }; - EE79B13D1CC8C435001029EF /* ShowBigPictureViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0F21CC8C435001029EF /* ShowBigPictureViewController.xib */; }; - EE79B13E1CC8C435001029EF /* VideoCommentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0F61CC8C435001029EF /* VideoCommentViewController.m */; }; - EE79B13F1CC8C435001029EF /* VideoCommentViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B0F71CC8C435001029EF /* VideoCommentViewController.xib */; }; - EE79B1401CC8C435001029EF /* VideoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0F91CC8C435001029EF /* VideoViewController.m */; }; - EE79B1411CC8C435001029EF /* TTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0FC1CC8C435001029EF /* TTVideo.m */; }; - EE79B1421CC8C435001029EF /* TTVideoComment.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B0FE1CC8C435001029EF /* TTVideoComment.m */; }; - EE79B1431CC8C435001029EF /* TTVideoUser.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B1001CC8C435001029EF /* TTVideoUser.m */; }; - EE79B1441CC8C435001029EF /* FullViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B1031CC8C435001029EF /* FullViewController.m */; }; - EE79B1451CC8C435001029EF /* VideoPlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B1051CC8C435001029EF /* VideoPlayView.m */; }; - EE79B1461CC8C435001029EF /* VideoPlayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B1061CC8C435001029EF /* VideoPlayView.xib */; }; - EE79B1471CC8C435001029EF /* VideoCommentCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B1091CC8C435001029EF /* VideoCommentCell.m */; }; - EE79B1481CC8C435001029EF /* VideoCommentCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B10A1CC8C435001029EF /* VideoCommentCell.xib */; }; - EE79B1491CC8C435001029EF /* VideoTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE79B10C1CC8C435001029EF /* VideoTableViewCell.m */; }; - EE79B14A1CC8C435001029EF /* VideoTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EE79B10D1CC8C435001029EF /* VideoTableViewCell.xib */; }; - EE8A19351D5AF9FD00BAE119 /* UserInfoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE8A19341D5AF9FD00BAE119 /* UserInfoCell.m */; }; - EE8A19381D5B04C800BAE119 /* SwitchCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE8A19371D5B04C800BAE119 /* SwitchCell.m */; }; - EE8A193B1D5B2BDD00BAE119 /* TwoLabelCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE8A193A1D5B2BDD00BAE119 /* TwoLabelCell.m */; }; - EE8A193E1D5B46CE00BAE119 /* DisclosureCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EE8A193D1D5B46CE00BAE119 /* DisclosureCell.m */; }; - EE8A194C1D5C476C00BAE119 /* TTNetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EE8A194B1D5C476C00BAE119 /* TTNetworkManager.m */; }; - EEB32A4B1CD38CAF00FF0AB8 /* TTTopChannelContianerView.m in Sources */ = {isa = PBXBuildFile; fileRef = EEB32A4A1CD38CAF00FF0AB8 /* TTTopChannelContianerView.m */; }; - EEB3DF6F1D8ED0130039ABF5 /* SXNewsEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = EEB3DF691D8ED0130039ABF5 /* SXNewsEntity.m */; }; - EEB3DF761D8ED3730039ABF5 /* SXNetworkTools.m in Sources */ = {isa = PBXBuildFile; fileRef = EEB3DF751D8ED3730039ABF5 /* SXNetworkTools.m */; }; - EEB3DF9C1D8F913A0039ABF5 /* BigPictureTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EEB3DF9A1D8F913A0039ABF5 /* BigPictureTableViewCell.m */; }; - EEB3DF9D1D8F913A0039ABF5 /* BigPictureTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EEB3DF9B1D8F913A0039ABF5 /* BigPictureTableViewCell.xib */; }; - EEB3DFA11D8F92010039ABF5 /* TopPictureTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EEB3DF9F1D8F92010039ABF5 /* TopPictureTableViewCell.m */; }; - EEB3DFA21D8F92010039ABF5 /* TopPictureTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EEB3DFA01D8F92010039ABF5 /* TopPictureTableViewCell.xib */; }; - EEB3DFA91D8F92920039ABF5 /* TopTextTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EEB3DFA71D8F92920039ABF5 /* TopTextTableViewCell.m */; }; - EEB3DFAA1D8F92920039ABF5 /* TopTextTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = EEB3DFA81D8F92920039ABF5 /* TopTextTableViewCell.xib */; }; - EEC791841CD1AEB8008B7C2D /* TTImageCyclePlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = EEC791831CD1AEB8008B7C2D /* TTImageCyclePlayView.m */; }; - EEE3E81E1CD5AD3E00D699E0 /* ChannelsSectionHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = EEE3E81D1CD5AD3E00D699E0 /* ChannelsSectionHeaderView.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - EE4635DD1CC72B8A00DB248F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = EE4635BB1CC72B8900DB248F /* Project object */; - proxyType = 1; - remoteGlobalIDString = EE4635C21CC72B8900DB248F; - remoteInfo = TTNews; - }; - EE4635E81CC72B8A00DB248F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = EE4635BB1CC72B8900DB248F /* Project object */; - proxyType = 1; - remoteGlobalIDString = EE4635C21CC72B8900DB248F; - remoteInfo = TTNews; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 53428844E2216BE5C197C066 /* Pods-TTNews.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TTNews.release.xcconfig"; path = "Pods/Target Support Files/Pods-TTNews/Pods-TTNews.release.xcconfig"; sourceTree = ""; }; - 59D897E651F8649ACCCD036B /* libPods-TTNews.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-TTNews.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 9D08A2A40F47DA39E7253110 /* Pods-TTNews.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TTNews.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TTNews/Pods-TTNews.debug.xcconfig"; sourceTree = ""; }; - EE3A071D1CDA1DAD0024C37A /* ContentTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContentTableViewController.m; sourceTree = ""; }; - EE4635C31CC72B8900DB248F /* TTNews.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TTNews.app; sourceTree = BUILT_PRODUCTS_DIR; }; - EE4635C71CC72B8900DB248F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - EE4635D21CC72B8900DB248F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - EE4635D71CC72B8900DB248F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - EE4635DC1CC72B8A00DB248F /* TTNewsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TTNewsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - EE4635E01CC72B8A00DB248F /* TTNewsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TTNewsTests.m; sourceTree = ""; }; - EE4635E21CC72B8A00DB248F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - EE4635E71CC72B8A00DB248F /* TTNewsUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TTNewsUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - EE4635EB1CC72B8A00DB248F /* TTNewsUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TTNewsUITests.m; sourceTree = ""; }; - EE4635ED1CC72B8A00DB248F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - EE4636B71CC7499D00DB248F /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; - EE4BEB5C1D5480E400B4A035 /* NewsURLs.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = NewsURLs.plist; sourceTree = ""; }; - EE79B08F1CC8C435001029EF /* AppInfoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppInfoViewController.h; sourceTree = ""; }; - EE79B0901CC8C435001029EF /* AppInfoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppInfoViewController.m; sourceTree = ""; }; - EE79B0911CC8C435001029EF /* AppInfoViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AppInfoViewController.xib; sourceTree = ""; }; - EE79B0921CC8C435001029EF /* EditUserInfoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditUserInfoViewController.h; sourceTree = ""; }; - EE79B0931CC8C435001029EF /* EditUserInfoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EditUserInfoViewController.m; sourceTree = ""; }; - EE79B0941CC8C435001029EF /* EditUserInfoViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EditUserInfoViewController.xib; sourceTree = ""; }; - EE79B0951CC8C435001029EF /* MeTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MeTableViewController.h; sourceTree = ""; }; - EE79B0961CC8C435001029EF /* MeTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MeTableViewController.m; sourceTree = ""; }; - EE79B0971CC8C435001029EF /* SendFeedbackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendFeedbackViewController.h; sourceTree = ""; }; - EE79B0981CC8C435001029EF /* SendFeedbackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SendFeedbackViewController.m; sourceTree = ""; }; - EE79B09D1CC8C435001029EF /* ContentTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentTableViewController.h; sourceTree = ""; }; - EE79B09F1CC8C435001029EF /* DetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetailViewController.h; sourceTree = ""; }; - EE79B0A01CC8C435001029EF /* DetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DetailViewController.m; sourceTree = ""; }; - EE79B0A11CC8C435001029EF /* NewsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NewsViewController.h; sourceTree = ""; }; - EE79B0A21CC8C435001029EF /* NewsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NewsViewController.m; sourceTree = ""; }; - EE79B0A31CC8C435001029EF /* ShowMultiPictureViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShowMultiPictureViewController.h; sourceTree = ""; }; - EE79B0A41CC8C435001029EF /* ShowMultiPictureViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShowMultiPictureViewController.m; sourceTree = ""; }; - EE79B0A61CC8C435001029EF /* TTHeaderNews.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTHeaderNews.h; sourceTree = ""; }; - EE79B0A71CC8C435001029EF /* TTHeaderNews.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTHeaderNews.m; sourceTree = ""; }; - EE79B0A81CC8C435001029EF /* TTNormalNews.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTNormalNews.h; sourceTree = ""; }; - EE79B0A91CC8C435001029EF /* TTNormalNews.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTNormalNews.m; sourceTree = ""; }; - EE79B0AB1CC8C435001029EF /* ChannelCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChannelCollectionViewCell.h; sourceTree = ""; }; - EE79B0AC1CC8C435001029EF /* ChannelCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChannelCollectionViewCell.m; sourceTree = ""; }; - EE79B0AD1CC8C435001029EF /* ChannelCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ChannelCollectionViewCell.xib; sourceTree = ""; }; - EE79B0AE1CC8C435001029EF /* MultiPictureTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiPictureTableViewCell.h; sourceTree = ""; }; - EE79B0AF1CC8C435001029EF /* MultiPictureTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultiPictureTableViewCell.m; sourceTree = ""; }; - EE79B0B01CC8C435001029EF /* MultiPictureTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MultiPictureTableViewCell.xib; sourceTree = ""; }; - EE79B0B11CC8C435001029EF /* NoPictureNewsTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoPictureNewsTableViewCell.h; sourceTree = ""; }; - EE79B0B21CC8C435001029EF /* NoPictureNewsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoPictureNewsTableViewCell.m; sourceTree = ""; }; - EE79B0B31CC8C435001029EF /* NoPictureNewsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NoPictureNewsTableViewCell.xib; sourceTree = ""; }; - EE79B0B41CC8C435001029EF /* SinglePictureNewsTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SinglePictureNewsTableViewCell.h; sourceTree = ""; }; - EE79B0B51CC8C435001029EF /* SinglePictureNewsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SinglePictureNewsTableViewCell.m; sourceTree = ""; }; - EE79B0B61CC8C435001029EF /* SinglePictureNewsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SinglePictureNewsTableViewCell.xib; sourceTree = ""; }; - EE79B0B91CC8C435001029EF /* Foundation+Log.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Foundation+Log.m"; sourceTree = ""; }; - EE79B0BA1CC8C435001029EF /* NSDate+Extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+Extension.h"; sourceTree = ""; }; - EE79B0BB1CC8C435001029EF /* NSDate+Extension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+Extension.m"; sourceTree = ""; }; - EE79B0BC1CC8C435001029EF /* UIBarButtonItem+Extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+Extension.h"; sourceTree = ""; }; - EE79B0BD1CC8C435001029EF /* UIBarButtonItem+Extension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+Extension.m"; sourceTree = ""; }; - EE79B0BE1CC8C435001029EF /* UIImage+Extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+Extension.h"; sourceTree = ""; }; - EE79B0BF1CC8C435001029EF /* UIImage+Extension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Extension.m"; sourceTree = ""; }; - EE79B0C01CC8C435001029EF /* UIImageView+Extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImageView+Extension.h"; sourceTree = ""; }; - EE79B0C11CC8C435001029EF /* UIImageView+Extension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+Extension.m"; sourceTree = ""; }; - EE79B0C21CC8C435001029EF /* UIView+Extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Extension.h"; sourceTree = ""; }; - EE79B0C31CC8C435001029EF /* UIView+Extension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Extension.m"; sourceTree = ""; }; - EE79B0C51CC8C435001029EF /* TTConst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTConst.h; path = ../Http/TTConst.h; sourceTree = ""; }; - EE79B0C61CC8C435001029EF /* TTConst.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TTConst.m; path = ../Http/TTConst.m; sourceTree = ""; }; - EE79B0C71CC8C435001029EF /* TTDataTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTDataTool.h; sourceTree = ""; }; - EE79B0C81CC8C435001029EF /* TTDataTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTDataTool.m; sourceTree = ""; }; - EE79B0C91CC8C435001029EF /* TTNormalNewsFetchDataParameter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTNormalNewsFetchDataParameter.h; sourceTree = ""; }; - EE79B0CA1CC8C435001029EF /* TTNormalNewsFetchDataParameter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTNormalNewsFetchDataParameter.m; sourceTree = ""; }; - EE79B0CB1CC8C435001029EF /* TTPictureFetchDataParameter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTPictureFetchDataParameter.h; sourceTree = ""; }; - EE79B0CC1CC8C435001029EF /* TTPictureFetchDataParameter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTPictureFetchDataParameter.m; sourceTree = ""; }; - EE79B0CD1CC8C435001029EF /* TTVideoFetchDataParameter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTVideoFetchDataParameter.h; sourceTree = ""; }; - EE79B0CE1CC8C435001029EF /* TTVideoFetchDataParameter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTVideoFetchDataParameter.m; sourceTree = ""; }; - EE79B0D01CC8C435001029EF /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - EE79B0D11CC8C435001029EF /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - EE79B0D21CC8C435001029EF /* TTNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTNavigationController.h; sourceTree = ""; }; - EE79B0D31CC8C435001029EF /* TTNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTNavigationController.m; sourceTree = ""; }; - EE79B0D41CC8C435001029EF /* TTTabBarController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTTabBarController.h; sourceTree = ""; }; - EE79B0D51CC8C435001029EF /* TTTabBarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTTabBarController.m; sourceTree = ""; }; - EE79B0D71CC8C435001029EF /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; - EE79B0D81CC8C435001029EF /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; - EE79B0D91CC8C435001029EF /* TTJudgeNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTJudgeNetworking.h; sourceTree = ""; }; - EE79B0DA1CC8C435001029EF /* TTJudgeNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTJudgeNetworking.m; sourceTree = ""; }; - EE79B0DD1CC8C435001029EF /* PictureCommentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PictureCommentViewController.h; sourceTree = ""; }; - EE79B0DE1CC8C435001029EF /* PictureCommentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PictureCommentViewController.m; sourceTree = ""; }; - EE79B0DF1CC8C435001029EF /* PictureCommentViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PictureCommentViewController.xib; sourceTree = ""; }; - EE79B0E01CC8C435001029EF /* PictureViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PictureViewController.h; sourceTree = ""; }; - EE79B0E11CC8C435001029EF /* PictureViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PictureViewController.m; sourceTree = ""; }; - EE79B0E31CC8C435001029EF /* TTPicture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTPicture.h; sourceTree = ""; }; - EE79B0E41CC8C435001029EF /* TTPicture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTPicture.m; sourceTree = ""; }; - EE79B0E51CC8C435001029EF /* TTPictureComment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTPictureComment.h; sourceTree = ""; }; - EE79B0E61CC8C435001029EF /* TTPictureComment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTPictureComment.m; sourceTree = ""; }; - EE79B0E71CC8C435001029EF /* TTPictureUser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTPictureUser.h; sourceTree = ""; }; - EE79B0E81CC8C435001029EF /* TTPictureUser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTPictureUser.m; sourceTree = ""; }; - EE79B0EA1CC8C435001029EF /* PictureCommentCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PictureCommentCell.h; sourceTree = ""; }; - EE79B0EB1CC8C435001029EF /* PictureCommentCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PictureCommentCell.m; sourceTree = ""; }; - EE79B0EC1CC8C435001029EF /* PictureCommentCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PictureCommentCell.xib; sourceTree = ""; }; - EE79B0ED1CC8C435001029EF /* PictureTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PictureTableViewCell.h; sourceTree = ""; }; - EE79B0EE1CC8C435001029EF /* PictureTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PictureTableViewCell.m; sourceTree = ""; }; - EE79B0EF1CC8C435001029EF /* PictureTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PictureTableViewCell.xib; sourceTree = ""; }; - EE79B0F01CC8C435001029EF /* ShowBigPictureViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ShowBigPictureViewController.h; path = ../View/ShowBigPictureViewController.h; sourceTree = ""; }; - EE79B0F11CC8C435001029EF /* ShowBigPictureViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ShowBigPictureViewController.m; path = ../View/ShowBigPictureViewController.m; sourceTree = ""; }; - EE79B0F21CC8C435001029EF /* ShowBigPictureViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ShowBigPictureViewController.xib; path = ../View/ShowBigPictureViewController.xib; sourceTree = ""; }; - EE79B0F51CC8C435001029EF /* VideoCommentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoCommentViewController.h; sourceTree = ""; }; - EE79B0F61CC8C435001029EF /* VideoCommentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoCommentViewController.m; sourceTree = ""; }; - EE79B0F71CC8C435001029EF /* VideoCommentViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VideoCommentViewController.xib; sourceTree = ""; }; - EE79B0F81CC8C435001029EF /* VideoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoViewController.h; sourceTree = ""; }; - EE79B0F91CC8C435001029EF /* VideoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoViewController.m; sourceTree = ""; }; - EE79B0FB1CC8C435001029EF /* TTVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTVideo.h; sourceTree = ""; }; - EE79B0FC1CC8C435001029EF /* TTVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTVideo.m; sourceTree = ""; }; - EE79B0FD1CC8C435001029EF /* TTVideoComment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTVideoComment.h; sourceTree = ""; }; - EE79B0FE1CC8C435001029EF /* TTVideoComment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTVideoComment.m; sourceTree = ""; }; - EE79B0FF1CC8C435001029EF /* TTVideoUser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTVideoUser.h; sourceTree = ""; }; - EE79B1001CC8C435001029EF /* TTVideoUser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTVideoUser.m; sourceTree = ""; }; - EE79B1021CC8C435001029EF /* FullViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FullViewController.h; sourceTree = ""; }; - EE79B1031CC8C435001029EF /* FullViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FullViewController.m; sourceTree = ""; }; - EE79B1041CC8C435001029EF /* VideoPlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoPlayView.h; sourceTree = ""; }; - EE79B1051CC8C435001029EF /* VideoPlayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoPlayView.m; sourceTree = ""; }; - EE79B1061CC8C435001029EF /* VideoPlayView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VideoPlayView.xib; sourceTree = ""; }; - EE79B1081CC8C435001029EF /* VideoCommentCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoCommentCell.h; sourceTree = ""; }; - EE79B1091CC8C435001029EF /* VideoCommentCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoCommentCell.m; sourceTree = ""; }; - EE79B10A1CC8C435001029EF /* VideoCommentCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VideoCommentCell.xib; sourceTree = ""; }; - EE79B10B1CC8C435001029EF /* VideoTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoTableViewCell.h; sourceTree = ""; }; - EE79B10C1CC8C435001029EF /* VideoTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoTableViewCell.m; sourceTree = ""; }; - EE79B10D1CC8C435001029EF /* VideoTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VideoTableViewCell.xib; sourceTree = ""; }; - EE8A19331D5AF9FD00BAE119 /* UserInfoCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserInfoCell.h; sourceTree = ""; }; - EE8A19341D5AF9FD00BAE119 /* UserInfoCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UserInfoCell.m; sourceTree = ""; }; - EE8A19361D5B04C800BAE119 /* SwitchCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwitchCell.h; sourceTree = ""; }; - EE8A19371D5B04C800BAE119 /* SwitchCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwitchCell.m; sourceTree = ""; }; - EE8A19391D5B2BDD00BAE119 /* TwoLabelCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TwoLabelCell.h; sourceTree = ""; }; - EE8A193A1D5B2BDD00BAE119 /* TwoLabelCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TwoLabelCell.m; sourceTree = ""; }; - EE8A193C1D5B46CE00BAE119 /* DisclosureCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DisclosureCell.h; sourceTree = ""; }; - EE8A193D1D5B46CE00BAE119 /* DisclosureCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DisclosureCell.m; sourceTree = ""; }; - EE8A194A1D5C476C00BAE119 /* TTNetworkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTNetworkManager.h; sourceTree = ""; }; - EE8A194B1D5C476C00BAE119 /* TTNetworkManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTNetworkManager.m; sourceTree = ""; }; - EEB32A491CD38CAF00FF0AB8 /* TTTopChannelContianerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTTopChannelContianerView.h; sourceTree = ""; }; - EEB32A4A1CD38CAF00FF0AB8 /* TTTopChannelContianerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTTopChannelContianerView.m; sourceTree = ""; }; - EEB3DF681D8ED0130039ABF5 /* SXNewsEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SXNewsEntity.h; path = ../Controller/SXNewsEntity.h; sourceTree = ""; }; - EEB3DF691D8ED0130039ABF5 /* SXNewsEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SXNewsEntity.m; path = ../Controller/SXNewsEntity.m; sourceTree = ""; }; - EEB3DF741D8ED3730039ABF5 /* SXNetworkTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SXNetworkTools.h; path = ../../News/Controller/SXNetworkTools.h; sourceTree = ""; }; - EEB3DF751D8ED3730039ABF5 /* SXNetworkTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SXNetworkTools.m; path = ../../News/Controller/SXNetworkTools.m; sourceTree = ""; }; - EEB3DF991D8F913A0039ABF5 /* BigPictureTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BigPictureTableViewCell.h; sourceTree = ""; }; - EEB3DF9A1D8F913A0039ABF5 /* BigPictureTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BigPictureTableViewCell.m; sourceTree = ""; }; - EEB3DF9B1D8F913A0039ABF5 /* BigPictureTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BigPictureTableViewCell.xib; sourceTree = ""; }; - EEB3DF9E1D8F92010039ABF5 /* TopPictureTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopPictureTableViewCell.h; sourceTree = ""; }; - EEB3DF9F1D8F92010039ABF5 /* TopPictureTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopPictureTableViewCell.m; sourceTree = ""; }; - EEB3DFA01D8F92010039ABF5 /* TopPictureTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TopPictureTableViewCell.xib; sourceTree = ""; }; - EEB3DFA61D8F92920039ABF5 /* TopTextTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopTextTableViewCell.h; sourceTree = ""; }; - EEB3DFA71D8F92920039ABF5 /* TopTextTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopTextTableViewCell.m; sourceTree = ""; }; - EEB3DFA81D8F92920039ABF5 /* TopTextTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TopTextTableViewCell.xib; sourceTree = ""; }; - EEC791821CD1AEB8008B7C2D /* TTImageCyclePlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTImageCyclePlayView.h; sourceTree = ""; }; - EEC791831CD1AEB8008B7C2D /* TTImageCyclePlayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTImageCyclePlayView.m; sourceTree = ""; }; - EEE3E81C1CD5AD3E00D699E0 /* ChannelsSectionHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChannelsSectionHeaderView.h; sourceTree = ""; }; - EEE3E81D1CD5AD3E00D699E0 /* ChannelsSectionHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChannelsSectionHeaderView.m; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - EE4635C01CC72B8900DB248F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 9D9B34292F23760DBD87E9B4 /* libPods-TTNews.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EE4635D91CC72B8A00DB248F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EE4635E41CC72B8A00DB248F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 3924BF4AEB33DC5601BA5628 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 59D897E651F8649ACCCD036B /* libPods-TTNews.a */, - ); - name = Frameworks; - sourceTree = ""; - }; - EE4635BA1CC72B8900DB248F = { - isa = PBXGroup; - children = ( - EE4635C51CC72B8900DB248F /* TTNews */, - EE4635DF1CC72B8A00DB248F /* TTNewsTests */, - EE4635EA1CC72B8A00DB248F /* TTNewsUITests */, - EE4635C41CC72B8900DB248F /* Products */, - FEE7BD5D4A5B10AF1F933FC9 /* Pods */, - 3924BF4AEB33DC5601BA5628 /* Frameworks */, - ); - sourceTree = ""; - }; - EE4635C41CC72B8900DB248F /* Products */ = { - isa = PBXGroup; - children = ( - EE4635C31CC72B8900DB248F /* TTNews.app */, - EE4635DC1CC72B8A00DB248F /* TTNewsTests.xctest */, - EE4635E71CC72B8A00DB248F /* TTNewsUITests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - EE4635C51CC72B8900DB248F /* TTNews */ = { - isa = PBXGroup; - children = ( - EE79B08C1CC8C435001029EF /* Classes */, - EE4635C61CC72B8900DB248F /* Supporting Files */, - ); - path = TTNews; - sourceTree = ""; - }; - EE4635C61CC72B8900DB248F /* Supporting Files */ = { - isa = PBXGroup; - children = ( - EE4636B71CC7499D00DB248F /* LaunchScreen.xib */, - EE4635D21CC72B8900DB248F /* Assets.xcassets */, - EE4635D71CC72B8900DB248F /* Info.plist */, - EE4635C71CC72B8900DB248F /* main.m */, - EE4BEB5C1D5480E400B4A035 /* NewsURLs.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - EE4635DF1CC72B8A00DB248F /* TTNewsTests */ = { - isa = PBXGroup; - children = ( - EE4635E01CC72B8A00DB248F /* TTNewsTests.m */, - EE4635E21CC72B8A00DB248F /* Info.plist */, - ); - path = TTNewsTests; - sourceTree = ""; - }; - EE4635EA1CC72B8A00DB248F /* TTNewsUITests */ = { - isa = PBXGroup; - children = ( - EE4635EB1CC72B8A00DB248F /* TTNewsUITests.m */, - EE4635ED1CC72B8A00DB248F /* Info.plist */, - ); - path = TTNewsUITests; - sourceTree = ""; - }; - EE79B08C1CC8C435001029EF /* Classes */ = { - isa = PBXGroup; - children = ( - EE79B09B1CC8C435001029EF /* News */, - EE79B0DB1CC8C435001029EF /* Picture */, - EE79B0F31CC8C435001029EF /* Video */, - EE79B08D1CC8C435001029EF /* Me */, - EE79B0B71CC8C435001029EF /* Other */, - ); - path = Classes; - sourceTree = ""; - }; - EE79B08D1CC8C435001029EF /* Me */ = { - isa = PBXGroup; - children = ( - EE79B08E1CC8C435001029EF /* Controller */, - EE79B0991CC8C435001029EF /* Model */, - EE79B09A1CC8C435001029EF /* View */, - ); - path = Me; - sourceTree = ""; - }; - EE79B08E1CC8C435001029EF /* Controller */ = { - isa = PBXGroup; - children = ( - EE79B0951CC8C435001029EF /* MeTableViewController.h */, - EE79B0961CC8C435001029EF /* MeTableViewController.m */, - EE79B0921CC8C435001029EF /* EditUserInfoViewController.h */, - EE79B0931CC8C435001029EF /* EditUserInfoViewController.m */, - EE79B0941CC8C435001029EF /* EditUserInfoViewController.xib */, - EE79B08F1CC8C435001029EF /* AppInfoViewController.h */, - EE79B0901CC8C435001029EF /* AppInfoViewController.m */, - EE79B0911CC8C435001029EF /* AppInfoViewController.xib */, - EE79B0971CC8C435001029EF /* SendFeedbackViewController.h */, - EE79B0981CC8C435001029EF /* SendFeedbackViewController.m */, - ); - path = Controller; - sourceTree = ""; - }; - EE79B0991CC8C435001029EF /* Model */ = { - isa = PBXGroup; - children = ( - ); - path = Model; - sourceTree = ""; - }; - EE79B09A1CC8C435001029EF /* View */ = { - isa = PBXGroup; - children = ( - EE8A19331D5AF9FD00BAE119 /* UserInfoCell.h */, - EE8A19341D5AF9FD00BAE119 /* UserInfoCell.m */, - EE8A19361D5B04C800BAE119 /* SwitchCell.h */, - EE8A19371D5B04C800BAE119 /* SwitchCell.m */, - EE8A19391D5B2BDD00BAE119 /* TwoLabelCell.h */, - EE8A193A1D5B2BDD00BAE119 /* TwoLabelCell.m */, - EE8A193C1D5B46CE00BAE119 /* DisclosureCell.h */, - EE8A193D1D5B46CE00BAE119 /* DisclosureCell.m */, - ); - path = View; - sourceTree = ""; - }; - EE79B09B1CC8C435001029EF /* News */ = { - isa = PBXGroup; - children = ( - EE79B09C1CC8C435001029EF /* Controller */, - EE79B0A51CC8C435001029EF /* Model */, - EE79B0AA1CC8C435001029EF /* View */, - EEB3DF7F1D8EDB550039ABF5 /* NewFile */, - ); - path = News; - sourceTree = ""; - }; - EE79B09C1CC8C435001029EF /* Controller */ = { - isa = PBXGroup; - children = ( - EE79B0A11CC8C435001029EF /* NewsViewController.h */, - EE79B0A21CC8C435001029EF /* NewsViewController.m */, - EE79B09D1CC8C435001029EF /* ContentTableViewController.h */, - EE3A071D1CDA1DAD0024C37A /* ContentTableViewController.m */, - EE79B09F1CC8C435001029EF /* DetailViewController.h */, - EE79B0A01CC8C435001029EF /* DetailViewController.m */, - EE79B0A31CC8C435001029EF /* ShowMultiPictureViewController.h */, - EE79B0A41CC8C435001029EF /* ShowMultiPictureViewController.m */, - ); - path = Controller; - sourceTree = ""; - }; - EE79B0A51CC8C435001029EF /* Model */ = { - isa = PBXGroup; - children = ( - EEB3DF681D8ED0130039ABF5 /* SXNewsEntity.h */, - EEB3DF691D8ED0130039ABF5 /* SXNewsEntity.m */, - EE79B0A61CC8C435001029EF /* TTHeaderNews.h */, - EE79B0A71CC8C435001029EF /* TTHeaderNews.m */, - EE79B0A81CC8C435001029EF /* TTNormalNews.h */, - EE79B0A91CC8C435001029EF /* TTNormalNews.m */, - ); - path = Model; - sourceTree = ""; - }; - EE79B0AA1CC8C435001029EF /* View */ = { - isa = PBXGroup; - children = ( - EEC791801CD1AE72008B7C2D /* Cell */, - EEB32A491CD38CAF00FF0AB8 /* TTTopChannelContianerView.h */, - EEB32A4A1CD38CAF00FF0AB8 /* TTTopChannelContianerView.m */, - EEC791821CD1AEB8008B7C2D /* TTImageCyclePlayView.h */, - EEC791831CD1AEB8008B7C2D /* TTImageCyclePlayView.m */, - EEE3E81C1CD5AD3E00D699E0 /* ChannelsSectionHeaderView.h */, - EEE3E81D1CD5AD3E00D699E0 /* ChannelsSectionHeaderView.m */, - ); - path = View; - sourceTree = ""; - }; - EE79B0B71CC8C435001029EF /* Other */ = { - isa = PBXGroup; - children = ( - EE79B0B81CC8C435001029EF /* Category */, - EE79B0C41CC8C435001029EF /* Http */, - EE79B0CF1CC8C435001029EF /* Main */, - EE79B0D61CC8C435001029EF /* NetWork */, - ); - path = Other; - sourceTree = ""; - }; - EE79B0B81CC8C435001029EF /* Category */ = { - isa = PBXGroup; - children = ( - EE79B0B91CC8C435001029EF /* Foundation+Log.m */, - EE79B0BA1CC8C435001029EF /* NSDate+Extension.h */, - EE79B0BB1CC8C435001029EF /* NSDate+Extension.m */, - EE79B0BC1CC8C435001029EF /* UIBarButtonItem+Extension.h */, - EE79B0BD1CC8C435001029EF /* UIBarButtonItem+Extension.m */, - EE79B0BE1CC8C435001029EF /* UIImage+Extension.h */, - EE79B0BF1CC8C435001029EF /* UIImage+Extension.m */, - EE79B0C01CC8C435001029EF /* UIImageView+Extension.h */, - EE79B0C11CC8C435001029EF /* UIImageView+Extension.m */, - EE79B0C21CC8C435001029EF /* UIView+Extension.h */, - EE79B0C31CC8C435001029EF /* UIView+Extension.m */, - ); - path = Category; - sourceTree = ""; - }; - EE79B0C41CC8C435001029EF /* Http */ = { - isa = PBXGroup; - children = ( - EEB3DF741D8ED3730039ABF5 /* SXNetworkTools.h */, - EEB3DF751D8ED3730039ABF5 /* SXNetworkTools.m */, - EE79B0C71CC8C435001029EF /* TTDataTool.h */, - EE79B0C81CC8C435001029EF /* TTDataTool.m */, - EE79B0C91CC8C435001029EF /* TTNormalNewsFetchDataParameter.h */, - EE79B0CA1CC8C435001029EF /* TTNormalNewsFetchDataParameter.m */, - EE79B0CB1CC8C435001029EF /* TTPictureFetchDataParameter.h */, - EE79B0CC1CC8C435001029EF /* TTPictureFetchDataParameter.m */, - EE79B0CD1CC8C435001029EF /* TTVideoFetchDataParameter.h */, - EE79B0CE1CC8C435001029EF /* TTVideoFetchDataParameter.m */, - EE8A194A1D5C476C00BAE119 /* TTNetworkManager.h */, - EE8A194B1D5C476C00BAE119 /* TTNetworkManager.m */, - ); - path = Http; - sourceTree = ""; - }; - EE79B0CF1CC8C435001029EF /* Main */ = { - isa = PBXGroup; - children = ( - EE79B0C51CC8C435001029EF /* TTConst.h */, - EE79B0C61CC8C435001029EF /* TTConst.m */, - EE79B0D01CC8C435001029EF /* AppDelegate.h */, - EE79B0D11CC8C435001029EF /* AppDelegate.m */, - EE79B0D21CC8C435001029EF /* TTNavigationController.h */, - EE79B0D31CC8C435001029EF /* TTNavigationController.m */, - EE79B0D41CC8C435001029EF /* TTTabBarController.h */, - EE79B0D51CC8C435001029EF /* TTTabBarController.m */, - ); - path = Main; - sourceTree = ""; - }; - EE79B0D61CC8C435001029EF /* NetWork */ = { - isa = PBXGroup; - children = ( - EE79B0D71CC8C435001029EF /* Reachability.h */, - EE79B0D81CC8C435001029EF /* Reachability.m */, - EE79B0D91CC8C435001029EF /* TTJudgeNetworking.h */, - EE79B0DA1CC8C435001029EF /* TTJudgeNetworking.m */, - ); - path = NetWork; - sourceTree = ""; - }; - EE79B0DB1CC8C435001029EF /* Picture */ = { - isa = PBXGroup; - children = ( - EE79B0DC1CC8C435001029EF /* Controller */, - EE79B0E21CC8C435001029EF /* Model */, - EE79B0E91CC8C435001029EF /* View */, - ); - path = Picture; - sourceTree = ""; - }; - EE79B0DC1CC8C435001029EF /* Controller */ = { - isa = PBXGroup; - children = ( - EE79B0E01CC8C435001029EF /* PictureViewController.h */, - EE79B0E11CC8C435001029EF /* PictureViewController.m */, - EE79B0DD1CC8C435001029EF /* PictureCommentViewController.h */, - EE79B0DE1CC8C435001029EF /* PictureCommentViewController.m */, - EE79B0DF1CC8C435001029EF /* PictureCommentViewController.xib */, - EE79B0F01CC8C435001029EF /* ShowBigPictureViewController.h */, - EE79B0F11CC8C435001029EF /* ShowBigPictureViewController.m */, - EE79B0F21CC8C435001029EF /* ShowBigPictureViewController.xib */, - ); - path = Controller; - sourceTree = ""; - }; - EE79B0E21CC8C435001029EF /* Model */ = { - isa = PBXGroup; - children = ( - EE79B0E31CC8C435001029EF /* TTPicture.h */, - EE79B0E41CC8C435001029EF /* TTPicture.m */, - EE79B0E51CC8C435001029EF /* TTPictureComment.h */, - EE79B0E61CC8C435001029EF /* TTPictureComment.m */, - EE79B0E71CC8C435001029EF /* TTPictureUser.h */, - EE79B0E81CC8C435001029EF /* TTPictureUser.m */, - ); - path = Model; - sourceTree = ""; - }; - EE79B0E91CC8C435001029EF /* View */ = { - isa = PBXGroup; - children = ( - EE79B0ED1CC8C435001029EF /* PictureTableViewCell.h */, - EE79B0EE1CC8C435001029EF /* PictureTableViewCell.m */, - EE79B0EF1CC8C435001029EF /* PictureTableViewCell.xib */, - EE79B0EA1CC8C435001029EF /* PictureCommentCell.h */, - EE79B0EB1CC8C435001029EF /* PictureCommentCell.m */, - EE79B0EC1CC8C435001029EF /* PictureCommentCell.xib */, - ); - path = View; - sourceTree = ""; - }; - EE79B0F31CC8C435001029EF /* Video */ = { - isa = PBXGroup; - children = ( - EE79B0F41CC8C435001029EF /* Controller */, - EE79B0FA1CC8C435001029EF /* Model */, - EE79B1011CC8C435001029EF /* VideoPlay */, - EE79B1071CC8C435001029EF /* View */, - ); - path = Video; - sourceTree = ""; - }; - EE79B0F41CC8C435001029EF /* Controller */ = { - isa = PBXGroup; - children = ( - EE79B0F81CC8C435001029EF /* VideoViewController.h */, - EE79B0F91CC8C435001029EF /* VideoViewController.m */, - EE79B0F51CC8C435001029EF /* VideoCommentViewController.h */, - EE79B0F61CC8C435001029EF /* VideoCommentViewController.m */, - EE79B0F71CC8C435001029EF /* VideoCommentViewController.xib */, - ); - path = Controller; - sourceTree = ""; - }; - EE79B0FA1CC8C435001029EF /* Model */ = { - isa = PBXGroup; - children = ( - EE79B0FB1CC8C435001029EF /* TTVideo.h */, - EE79B0FC1CC8C435001029EF /* TTVideo.m */, - EE79B0FD1CC8C435001029EF /* TTVideoComment.h */, - EE79B0FE1CC8C435001029EF /* TTVideoComment.m */, - EE79B0FF1CC8C435001029EF /* TTVideoUser.h */, - EE79B1001CC8C435001029EF /* TTVideoUser.m */, - ); - path = Model; - sourceTree = ""; - }; - EE79B1011CC8C435001029EF /* VideoPlay */ = { - isa = PBXGroup; - children = ( - EE79B1021CC8C435001029EF /* FullViewController.h */, - EE79B1031CC8C435001029EF /* FullViewController.m */, - EE79B1041CC8C435001029EF /* VideoPlayView.h */, - EE79B1051CC8C435001029EF /* VideoPlayView.m */, - EE79B1061CC8C435001029EF /* VideoPlayView.xib */, - ); - path = VideoPlay; - sourceTree = ""; - }; - EE79B1071CC8C435001029EF /* View */ = { - isa = PBXGroup; - children = ( - EE79B10B1CC8C435001029EF /* VideoTableViewCell.h */, - EE79B10C1CC8C435001029EF /* VideoTableViewCell.m */, - EE79B10D1CC8C435001029EF /* VideoTableViewCell.xib */, - EE79B1081CC8C435001029EF /* VideoCommentCell.h */, - EE79B1091CC8C435001029EF /* VideoCommentCell.m */, - EE79B10A1CC8C435001029EF /* VideoCommentCell.xib */, - ); - path = View; - sourceTree = ""; - }; - EEB3DF7F1D8EDB550039ABF5 /* NewFile */ = { - isa = PBXGroup; - children = ( - ); - name = NewFile; - sourceTree = ""; - }; - EEC791801CD1AE72008B7C2D /* Cell */ = { - isa = PBXGroup; - children = ( - EE79B0B11CC8C435001029EF /* NoPictureNewsTableViewCell.h */, - EE79B0B21CC8C435001029EF /* NoPictureNewsTableViewCell.m */, - EE79B0B31CC8C435001029EF /* NoPictureNewsTableViewCell.xib */, - EE79B0B41CC8C435001029EF /* SinglePictureNewsTableViewCell.h */, - EE79B0B51CC8C435001029EF /* SinglePictureNewsTableViewCell.m */, - EE79B0B61CC8C435001029EF /* SinglePictureNewsTableViewCell.xib */, - EE79B0AE1CC8C435001029EF /* MultiPictureTableViewCell.h */, - EE79B0AF1CC8C435001029EF /* MultiPictureTableViewCell.m */, - EE79B0B01CC8C435001029EF /* MultiPictureTableViewCell.xib */, - EEB3DF991D8F913A0039ABF5 /* BigPictureTableViewCell.h */, - EEB3DF9A1D8F913A0039ABF5 /* BigPictureTableViewCell.m */, - EEB3DF9B1D8F913A0039ABF5 /* BigPictureTableViewCell.xib */, - EEB3DF9E1D8F92010039ABF5 /* TopPictureTableViewCell.h */, - EEB3DF9F1D8F92010039ABF5 /* TopPictureTableViewCell.m */, - EEB3DFA01D8F92010039ABF5 /* TopPictureTableViewCell.xib */, - EEB3DFA61D8F92920039ABF5 /* TopTextTableViewCell.h */, - EEB3DFA71D8F92920039ABF5 /* TopTextTableViewCell.m */, - EEB3DFA81D8F92920039ABF5 /* TopTextTableViewCell.xib */, - EE79B0AB1CC8C435001029EF /* ChannelCollectionViewCell.h */, - EE79B0AC1CC8C435001029EF /* ChannelCollectionViewCell.m */, - EE79B0AD1CC8C435001029EF /* ChannelCollectionViewCell.xib */, - ); - name = Cell; - sourceTree = ""; - }; - FEE7BD5D4A5B10AF1F933FC9 /* Pods */ = { - isa = PBXGroup; - children = ( - 9D08A2A40F47DA39E7253110 /* Pods-TTNews.debug.xcconfig */, - 53428844E2216BE5C197C066 /* Pods-TTNews.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - EE4635C21CC72B8900DB248F /* TTNews */ = { - isa = PBXNativeTarget; - buildConfigurationList = EE4635F01CC72B8A00DB248F /* Build configuration list for PBXNativeTarget "TTNews" */; - buildPhases = ( - 1AE00DDF8EBBF681D93CF56F /* Check Pods Manifest.lock */, - EE4635BF1CC72B8900DB248F /* Sources */, - EE4635C01CC72B8900DB248F /* Frameworks */, - EE4635C11CC72B8900DB248F /* Resources */, - 60B51DF1B2ECD0D3BBF39900 /* Embed Pods Frameworks */, - FA83F04A92182C30BDA63B36 /* Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = TTNews; - productName = TTNews; - productReference = EE4635C31CC72B8900DB248F /* TTNews.app */; - productType = "com.apple.product-type.application"; - }; - EE4635DB1CC72B8A00DB248F /* TTNewsTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = EE4635F31CC72B8A00DB248F /* Build configuration list for PBXNativeTarget "TTNewsTests" */; - buildPhases = ( - EE4635D81CC72B8A00DB248F /* Sources */, - EE4635D91CC72B8A00DB248F /* Frameworks */, - EE4635DA1CC72B8A00DB248F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - EE4635DE1CC72B8A00DB248F /* PBXTargetDependency */, - ); - name = TTNewsTests; - productName = TTNewsTests; - productReference = EE4635DC1CC72B8A00DB248F /* TTNewsTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - EE4635E61CC72B8A00DB248F /* TTNewsUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = EE4635F61CC72B8A00DB248F /* Build configuration list for PBXNativeTarget "TTNewsUITests" */; - buildPhases = ( - EE4635E31CC72B8A00DB248F /* Sources */, - EE4635E41CC72B8A00DB248F /* Frameworks */, - EE4635E51CC72B8A00DB248F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - EE4635E91CC72B8A00DB248F /* PBXTargetDependency */, - ); - name = TTNewsUITests; - productName = TTNewsUITests; - productReference = EE4635E71CC72B8A00DB248F /* TTNewsUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - EE4635BB1CC72B8900DB248F /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0810; - ORGANIZATIONNAME = "瑞文戴尔"; - TargetAttributes = { - EE4635C21CC72B8900DB248F = { - CreatedOnToolsVersion = 7.3; - }; - EE4635DB1CC72B8A00DB248F = { - CreatedOnToolsVersion = 7.3; - DevelopmentTeam = FJ2RR7PUE2; - TestTargetID = EE4635C21CC72B8900DB248F; - }; - EE4635E61CC72B8A00DB248F = { - CreatedOnToolsVersion = 7.3; - DevelopmentTeam = FJ2RR7PUE2; - TestTargetID = EE4635C21CC72B8900DB248F; - }; - }; - }; - buildConfigurationList = EE4635BE1CC72B8900DB248F /* Build configuration list for PBXProject "TTNews" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = EE4635BA1CC72B8900DB248F; - productRefGroup = EE4635C41CC72B8900DB248F /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - EE4635C21CC72B8900DB248F /* TTNews */, - EE4635DB1CC72B8A00DB248F /* TTNewsTests */, - EE4635E61CC72B8A00DB248F /* TTNewsUITests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - EE4635C11CC72B8900DB248F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - EE4635D31CC72B8900DB248F /* Assets.xcassets in Resources */, - EE79B14A1CC8C435001029EF /* VideoTableViewCell.xib in Resources */, - EE79B11B1CC8C435001029EF /* ChannelCollectionViewCell.xib in Resources */, - EE79B13D1CC8C435001029EF /* ShowBigPictureViewController.xib in Resources */, - EE79B1391CC8C435001029EF /* PictureCommentCell.xib in Resources */, - EE79B1461CC8C435001029EF /* VideoPlayView.xib in Resources */, - EE79B11F1CC8C435001029EF /* NoPictureNewsTableViewCell.xib in Resources */, - EE4636B81CC7499D00DB248F /* LaunchScreen.xib in Resources */, - EE79B10F1CC8C435001029EF /* AppInfoViewController.xib in Resources */, - EEB3DF9D1D8F913A0039ABF5 /* BigPictureTableViewCell.xib in Resources */, - EE79B1211CC8C435001029EF /* SinglePictureNewsTableViewCell.xib in Resources */, - EE79B13B1CC8C435001029EF /* PictureTableViewCell.xib in Resources */, - EEB3DFAA1D8F92920039ABF5 /* TopTextTableViewCell.xib in Resources */, - EE79B11D1CC8C435001029EF /* MultiPictureTableViewCell.xib in Resources */, - EE79B1481CC8C435001029EF /* VideoCommentCell.xib in Resources */, - EEB3DFA21D8F92010039ABF5 /* TopPictureTableViewCell.xib in Resources */, - EE79B1331CC8C435001029EF /* PictureCommentViewController.xib in Resources */, - EE79B1111CC8C435001029EF /* EditUserInfoViewController.xib in Resources */, - EE79B13F1CC8C435001029EF /* VideoCommentViewController.xib in Resources */, - EE4BEB5D1D5480E400B4A035 /* NewsURLs.plist in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EE4635DA1CC72B8A00DB248F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EE4635E51CC72B8A00DB248F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 1AE00DDF8EBBF681D93CF56F /* Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Check Pods Manifest.lock"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; - showEnvVarsInLog = 0; - }; - 60B51DF1B2ECD0D3BBF39900 /* Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - FA83F04A92182C30BDA63B36 /* Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TTNews/Pods-TTNews-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - EE4635BF1CC72B8900DB248F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - EEB3DF6F1D8ED0130039ABF5 /* SXNewsEntity.m in Sources */, - EE79B1351CC8C435001029EF /* TTPicture.m in Sources */, - EE79B1381CC8C435001029EF /* PictureCommentCell.m in Sources */, - EE8A194C1D5C476C00BAE119 /* TTNetworkManager.m in Sources */, - EE79B10E1CC8C435001029EF /* AppInfoViewController.m in Sources */, - EE79B1281CC8C435001029EF /* TTConst.m in Sources */, - EE8A19351D5AF9FD00BAE119 /* UserInfoCell.m in Sources */, - EE79B12D1CC8C435001029EF /* AppDelegate.m in Sources */, - EE8A19381D5B04C800BAE119 /* SwitchCell.m in Sources */, - EE79B12E1CC8C435001029EF /* TTNavigationController.m in Sources */, - EE79B1301CC8C435001029EF /* Reachability.m in Sources */, - EE79B1411CC8C435001029EF /* TTVideo.m in Sources */, - EE79B1491CC8C435001029EF /* VideoTableViewCell.m in Sources */, - EE79B13E1CC8C435001029EF /* VideoCommentViewController.m in Sources */, - EE79B1361CC8C435001029EF /* TTPictureComment.m in Sources */, - EE79B11A1CC8C435001029EF /* ChannelCollectionViewCell.m in Sources */, - EE79B1371CC8C435001029EF /* TTPictureUser.m in Sources */, - EE79B1321CC8C435001029EF /* PictureCommentViewController.m in Sources */, - EE79B1271CC8C435001029EF /* UIView+Extension.m in Sources */, - EE79B1101CC8C435001029EF /* EditUserInfoViewController.m in Sources */, - EE79B13C1CC8C435001029EF /* ShowBigPictureViewController.m in Sources */, - EEB3DFA91D8F92920039ABF5 /* TopTextTableViewCell.m in Sources */, - EEE3E81E1CD5AD3E00D699E0 /* ChannelsSectionHeaderView.m in Sources */, - EE79B13A1CC8C435001029EF /* PictureTableViewCell.m in Sources */, - EE79B1341CC8C435001029EF /* PictureViewController.m in Sources */, - EE79B1181CC8C435001029EF /* TTHeaderNews.m in Sources */, - EE79B1161CC8C435001029EF /* NewsViewController.m in Sources */, - EE79B12C1CC8C435001029EF /* TTVideoFetchDataParameter.m in Sources */, - EE79B1241CC8C435001029EF /* UIBarButtonItem+Extension.m in Sources */, - EE79B12F1CC8C435001029EF /* TTTabBarController.m in Sources */, - EE3A071E1CDA1DAD0024C37A /* ContentTableViewController.m in Sources */, - EE79B1251CC8C435001029EF /* UIImage+Extension.m in Sources */, - EE79B11E1CC8C435001029EF /* NoPictureNewsTableViewCell.m in Sources */, - EE79B1471CC8C435001029EF /* VideoCommentCell.m in Sources */, - EE79B1291CC8C435001029EF /* TTDataTool.m in Sources */, - EE79B1201CC8C435001029EF /* SinglePictureNewsTableViewCell.m in Sources */, - EE79B1191CC8C435001029EF /* TTNormalNews.m in Sources */, - EE79B1401CC8C435001029EF /* VideoViewController.m in Sources */, - EE4635C81CC72B8900DB248F /* main.m in Sources */, - EEC791841CD1AEB8008B7C2D /* TTImageCyclePlayView.m in Sources */, - EE8A193B1D5B2BDD00BAE119 /* TwoLabelCell.m in Sources */, - EE79B1231CC8C435001029EF /* NSDate+Extension.m in Sources */, - EE79B11C1CC8C435001029EF /* MultiPictureTableViewCell.m in Sources */, - EEB3DFA11D8F92010039ABF5 /* TopPictureTableViewCell.m in Sources */, - EE79B1261CC8C435001029EF /* UIImageView+Extension.m in Sources */, - EE79B1441CC8C435001029EF /* FullViewController.m in Sources */, - EE79B1431CC8C435001029EF /* TTVideoUser.m in Sources */, - EE79B1121CC8C435001029EF /* MeTableViewController.m in Sources */, - EE79B1421CC8C435001029EF /* TTVideoComment.m in Sources */, - EEB32A4B1CD38CAF00FF0AB8 /* TTTopChannelContianerView.m in Sources */, - EE79B1451CC8C435001029EF /* VideoPlayView.m in Sources */, - EE79B12B1CC8C435001029EF /* TTPictureFetchDataParameter.m in Sources */, - EE79B1131CC8C435001029EF /* SendFeedbackViewController.m in Sources */, - EE79B12A1CC8C435001029EF /* TTNormalNewsFetchDataParameter.m in Sources */, - EE79B1171CC8C435001029EF /* ShowMultiPictureViewController.m in Sources */, - EE79B1151CC8C435001029EF /* DetailViewController.m in Sources */, - EE79B1221CC8C435001029EF /* Foundation+Log.m in Sources */, - EEB3DF9C1D8F913A0039ABF5 /* BigPictureTableViewCell.m in Sources */, - EE79B1311CC8C435001029EF /* TTJudgeNetworking.m in Sources */, - EE8A193E1D5B46CE00BAE119 /* DisclosureCell.m in Sources */, - EEB3DF761D8ED3730039ABF5 /* SXNetworkTools.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EE4635D81CC72B8A00DB248F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - EE4635E11CC72B8A00DB248F /* TTNewsTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EE4635E31CC72B8A00DB248F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - EE4635EC1CC72B8A00DB248F /* TTNewsUITests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - EE4635DE1CC72B8A00DB248F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = EE4635C21CC72B8900DB248F /* TTNews */; - targetProxy = EE4635DD1CC72B8A00DB248F /* PBXContainerItemProxy */; - }; - EE4635E91CC72B8A00DB248F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = EE4635C21CC72B8900DB248F /* TTNews */; - targetProxy = EE4635E81CC72B8A00DB248F /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - EE4635EE1CC72B8A00DB248F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - EE4635EF1CC72B8A00DB248F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - EE4635F11CC72B8A00DB248F /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9D08A2A40F47DA39E7253110 /* Pods-TTNews.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - INFOPLIST_FILE = TTNews/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = yangjunhui.TTNews12; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; - }; - name = Debug; - }; - EE4635F21CC72B8A00DB248F /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 53428844E2216BE5C197C066 /* Pods-TTNews.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - INFOPLIST_FILE = TTNews/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = yangjunhui.TTNews12; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; - }; - name = Release; - }; - EE4635F41CC72B8A00DB248F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - INFOPLIST_FILE = TTNewsTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = yangjunhui.TTNewsTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TTNews.app/TTNews"; - }; - name = Debug; - }; - EE4635F51CC72B8A00DB248F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - INFOPLIST_FILE = TTNewsTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = yangjunhui.TTNewsTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TTNews.app/TTNews"; - }; - name = Release; - }; - EE4635F71CC72B8A00DB248F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - INFOPLIST_FILE = TTNewsUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = yangjunhui.TTNewsUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = TTNews; - }; - name = Debug; - }; - EE4635F81CC72B8A00DB248F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - INFOPLIST_FILE = TTNewsUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = yangjunhui.TTNewsUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = TTNews; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - EE4635BE1CC72B8900DB248F /* Build configuration list for PBXProject "TTNews" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - EE4635EE1CC72B8A00DB248F /* Debug */, - EE4635EF1CC72B8A00DB248F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - EE4635F01CC72B8A00DB248F /* Build configuration list for PBXNativeTarget "TTNews" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - EE4635F11CC72B8A00DB248F /* Debug */, - EE4635F21CC72B8A00DB248F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - EE4635F31CC72B8A00DB248F /* Build configuration list for PBXNativeTarget "TTNewsTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - EE4635F41CC72B8A00DB248F /* Debug */, - EE4635F51CC72B8A00DB248F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - EE4635F61CC72B8A00DB248F /* Build configuration list for PBXNativeTarget "TTNewsUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - EE4635F71CC72B8A00DB248F /* Debug */, - EE4635F81CC72B8A00DB248F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = EE4635BB1CC72B8900DB248F /* Project object */; -} diff --git a/TTNews.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TTNews.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 0e691b3..0000000 --- a/TTNews.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/TTNews.xcodeproj/project.xcworkspace/xcuserdata/ruiwendaier.xcuserdatad/UserInterfaceState.xcuserstate b/TTNews.xcodeproj/project.xcworkspace/xcuserdata/ruiwendaier.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index d517b26..0000000 Binary files a/TTNews.xcodeproj/project.xcworkspace/xcuserdata/ruiwendaier.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/TTNews.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/TTNews.xcscheme b/TTNews.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/TTNews.xcscheme deleted file mode 100644 index 84d80a2..0000000 --- a/TTNews.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/TTNews.xcscheme +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/xcschememanagement.plist b/TTNews.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 3baeff1..0000000 --- a/TTNews.xcodeproj/xcuserdata/ruiwendaier.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - SchemeUserState - - TTNews.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - EE4635C21CC72B8900DB248F - - primary - - - EE4635DB1CC72B8A00DB248F - - primary - - - EE4635E61CC72B8A00DB248F - - primary - - - - - diff --git a/TTNews.xcworkspace/contents.xcworkspacedata b/TTNews.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index d281c03..0000000 --- a/TTNews.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/TTNews.xcworkspace/xcuserdata/ruiwendaier.xcuserdatad/UserInterfaceState.xcuserstate b/TTNews.xcworkspace/xcuserdata/ruiwendaier.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index d12653a..0000000 Binary files a/TTNews.xcworkspace/xcuserdata/ruiwendaier.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/TTNews.xcworkspace/xcuserdata/ruiwendaier.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/TTNews.xcworkspace/xcuserdata/ruiwendaier.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index 693dbc4..0000000 --- a/TTNews.xcworkspace/xcuserdata/ruiwendaier.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - diff --git a/TTNews/Assets.xcassets/AppIcon.appiconset/Contents.json b/TTNews/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 2fde950..0000000 --- a/TTNews/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "images" : [ - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "appicon副本-4.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "appicon.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "appicon副本-3.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "appicon副本-2.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "appicon副本-1.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "appicon副本.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "appicon副本 2.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "appicon副本 2-1.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "appicon副本 2-3.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "appicon副本 2-2.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "appicon副本-6.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "appicon副本-7.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "appicon副本-5.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon.png b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon.png deleted file mode 100644 index f2740cd..0000000 Binary files a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon.png and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2-1.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2-1.png" deleted file mode 100644 index 75bd2c2..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2-1.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2-2.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2-2.png" deleted file mode 100644 index fe9c29b..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2-2.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2-3.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2-3.png" deleted file mode 100644 index 57a751e..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2-3.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2.png" deleted file mode 100644 index c07e532..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254 2.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-1.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-1.png" deleted file mode 100644 index d9579c6..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-1.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-2.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-2.png" deleted file mode 100644 index d9579c6..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-2.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-3.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-3.png" deleted file mode 100644 index 3da7500..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-3.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-4.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-4.png" deleted file mode 100644 index 2f5b63e..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-4.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-5.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-5.png" deleted file mode 100644 index 96c8998..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-5.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-6.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-6.png" deleted file mode 100644 index 1875b82..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-6.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-7.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-7.png" deleted file mode 100644 index 4c8e495..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254-7.png" and /dev/null differ diff --git "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254.png" "b/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254.png" deleted file mode 100644 index 3b98e78..0000000 Binary files "a/TTNews/Assets.xcassets/AppIcon.appiconset/appicon\345\211\257\346\234\254.png" and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/Contents.json b/TTNews/Assets.xcassets/Comment/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/TTNews/Assets.xcassets/Comment/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Contents.json b/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Contents.json deleted file mode 100644 index f6b7c9e..0000000 --- a/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Profile_manIcon.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Profile_manIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Profile_manIcon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Profile_manIcon.png b/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Profile_manIcon.png deleted file mode 100644 index 1b0460d..0000000 Binary files a/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Profile_manIcon.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Profile_manIcon@2x.png b/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Profile_manIcon@2x.png deleted file mode 100644 index 6dc0454..0000000 Binary files a/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Profile_manIcon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Profile_manIcon@3x.png b/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Profile_manIcon@3x.png deleted file mode 100644 index e13b7ef..0000000 Binary files a/TTNews/Assets.xcassets/Comment/Profile_manIcon.imageset/Profile_manIcon@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Contents.json b/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Contents.json deleted file mode 100644 index a8d1931..0000000 --- a/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Profile_womanIcon.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Profile_womanIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Profile_womanIcon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Profile_womanIcon.png b/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Profile_womanIcon.png deleted file mode 100644 index 3090cc5..0000000 Binary files a/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Profile_womanIcon.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Profile_womanIcon@2x.png b/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Profile_womanIcon@2x.png deleted file mode 100644 index 106b8ce..0000000 Binary files a/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Profile_womanIcon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Profile_womanIcon@3x.png b/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Profile_womanIcon@3x.png deleted file mode 100644 index a520b9c..0000000 Binary files a/TTNews/Assets.xcassets/Comment/Profile_womanIcon.imageset/Profile_womanIcon@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/Contents.json b/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/Contents.json deleted file mode 100644 index ac67ff8..0000000 --- a/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "comment-bar-voice-click.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "comment-bar-voice-click@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "comment-bar-voice-click@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/comment-bar-voice-click.png b/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/comment-bar-voice-click.png deleted file mode 100644 index a4b40de..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/comment-bar-voice-click.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/comment-bar-voice-click@2x.png b/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/comment-bar-voice-click@2x.png deleted file mode 100644 index 0b93305..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/comment-bar-voice-click@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/comment-bar-voice-click@3x.png b/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/comment-bar-voice-click@3x.png deleted file mode 100644 index cd14936..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment-bar-voice-click.imageset/comment-bar-voice-click@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/Contents.json b/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/Contents.json deleted file mode 100644 index 9022838..0000000 --- a/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "comment-bar-voice.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "comment-bar-voice@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "comment-bar-voice@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/comment-bar-voice.png b/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/comment-bar-voice.png deleted file mode 100644 index 5305785..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/comment-bar-voice.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/comment-bar-voice@2x.png b/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/comment-bar-voice@2x.png deleted file mode 100644 index 6ef16ef..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/comment-bar-voice@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/comment-bar-voice@3x.png b/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/comment-bar-voice@3x.png deleted file mode 100644 index cacd959..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment-bar-voice.imageset/comment-bar-voice@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/commentLikeButton.imageset/Contents.json b/TTNews/Assets.xcassets/Comment/commentLikeButton.imageset/Contents.json deleted file mode 100644 index cfb5925..0000000 --- a/TTNews/Assets.xcassets/Comment/commentLikeButton.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "commentLikeButton.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "commentLikeButton@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Comment/commentLikeButton.imageset/commentLikeButton.png b/TTNews/Assets.xcassets/Comment/commentLikeButton.imageset/commentLikeButton.png deleted file mode 100644 index 48b2e6d..0000000 Binary files a/TTNews/Assets.xcassets/Comment/commentLikeButton.imageset/commentLikeButton.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/commentLikeButton.imageset/commentLikeButton@2x.png b/TTNews/Assets.xcassets/Comment/commentLikeButton.imageset/commentLikeButton@2x.png deleted file mode 100644 index 877c27a..0000000 Binary files a/TTNews/Assets.xcassets/Comment/commentLikeButton.imageset/commentLikeButton@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/commentLikeButtonClick.imageset/Contents.json b/TTNews/Assets.xcassets/Comment/commentLikeButtonClick.imageset/Contents.json deleted file mode 100644 index 96e8cf8..0000000 --- a/TTNews/Assets.xcassets/Comment/commentLikeButtonClick.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "commentLikeButtonClick.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "commentLikeButtonClick@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Comment/commentLikeButtonClick.imageset/commentLikeButtonClick.png b/TTNews/Assets.xcassets/Comment/commentLikeButtonClick.imageset/commentLikeButtonClick.png deleted file mode 100644 index 9ba719f..0000000 Binary files a/TTNews/Assets.xcassets/Comment/commentLikeButtonClick.imageset/commentLikeButtonClick.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/commentLikeButtonClick.imageset/commentLikeButtonClick@2x.png b/TTNews/Assets.xcassets/Comment/commentLikeButtonClick.imageset/commentLikeButtonClick@2x.png deleted file mode 100644 index 0ebb1f7..0000000 Binary files a/TTNews/Assets.xcassets/Comment/commentLikeButtonClick.imageset/commentLikeButtonClick@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/Contents.json b/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/Contents.json deleted file mode 100644 index 960c512..0000000 --- a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "comment_bar_at_icon.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "comment_bar_at_icon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "comment_bar_at_icon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/comment_bar_at_icon.png b/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/comment_bar_at_icon.png deleted file mode 100644 index a45b834..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/comment_bar_at_icon.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/comment_bar_at_icon@2x.png b/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/comment_bar_at_icon@2x.png deleted file mode 100644 index fa793cb..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/comment_bar_at_icon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/comment_bar_at_icon@3x.png b/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/comment_bar_at_icon@3x.png deleted file mode 100644 index 61990b1..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon.imageset/comment_bar_at_icon@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/Contents.json b/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/Contents.json deleted file mode 100644 index c8fd95e..0000000 --- a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "comment_bar_at_icon_click.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "comment_bar_at_icon_click@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "comment_bar_at_icon_click@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/comment_bar_at_icon_click.png b/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/comment_bar_at_icon_click.png deleted file mode 100644 index 2927a5f..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/comment_bar_at_icon_click.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/comment_bar_at_icon_click@2x.png b/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/comment_bar_at_icon_click@2x.png deleted file mode 100644 index 23bfbfa..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/comment_bar_at_icon_click@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/comment_bar_at_icon_click@3x.png b/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/comment_bar_at_icon_click@3x.png deleted file mode 100644 index e43e043..0000000 Binary files a/TTNews/Assets.xcassets/Comment/comment_bar_at_icon_click.imageset/comment_bar_at_icon_click@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Contents.json b/TTNews/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/TTNews/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/Contents.json b/TTNews/Assets.xcassets/Essence/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/TTNews/Assets.xcassets/Essence/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Contents.json deleted file mode 100644 index 056b6de..0000000 --- a/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Profile_AddV_authen.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Profile_AddV_authen@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Profile_AddV_authen@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Profile_AddV_authen.png b/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Profile_AddV_authen.png deleted file mode 100644 index 8f364c1..0000000 Binary files a/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Profile_AddV_authen.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Profile_AddV_authen@2x.png b/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Profile_AddV_authen@2x.png deleted file mode 100644 index a0501be..0000000 Binary files a/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Profile_AddV_authen@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Profile_AddV_authen@3x.png b/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Profile_AddV_authen@3x.png deleted file mode 100644 index 4cb0341..0000000 Binary files a/TTNews/Assets.xcassets/Essence/Profile_AddV_authen.imageset/Profile_AddV_authen@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/common-gif.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/common-gif.imageset/Contents.json deleted file mode 100644 index 2d89748..0000000 --- a/TTNews/Assets.xcassets/Essence/common-gif.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "common-gif.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "common-gif@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/common-gif.imageset/common-gif.png b/TTNews/Assets.xcassets/Essence/common-gif.imageset/common-gif.png deleted file mode 100644 index cb2454f..0000000 Binary files a/TTNews/Assets.xcassets/Essence/common-gif.imageset/common-gif.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/common-gif.imageset/common-gif@2x.png b/TTNews/Assets.xcassets/Essence/common-gif.imageset/common-gif@2x.png deleted file mode 100644 index 46e8891..0000000 Binary files a/TTNews/Assets.xcassets/Essence/common-gif.imageset/common-gif@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/Contents.json deleted file mode 100644 index 78027f8..0000000 --- a/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "defaultUserIcon.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "defaultUserIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "defaultUserIcon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/defaultUserIcon.png b/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/defaultUserIcon.png deleted file mode 100644 index cc179ce..0000000 Binary files a/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/defaultUserIcon.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/defaultUserIcon@2x.png b/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/defaultUserIcon@2x.png deleted file mode 100644 index 8449f8b..0000000 Binary files a/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/defaultUserIcon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/defaultUserIcon@3x.png b/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/defaultUserIcon@3x.png deleted file mode 100644 index 533adf4..0000000 Binary files a/TTNews/Assets.xcassets/Essence/defaultUserIcon.imageset/defaultUserIcon@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellBackground.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/mainCellBackground.imageset/Contents.json deleted file mode 100644 index 1e43548..0000000 --- a/TTNews/Assets.xcassets/Essence/mainCellBackground.imageset/Contents.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "images" : [ - { - "resizing" : { - "mode" : "9-part", - "center" : { - "mode" : "tile", - "width" : 1, - "height" : 1 - }, - "cap-insets" : { - "bottom" : 2, - "top" : 2, - "right" : 25, - "left" : 24 - } - }, - "idiom" : "universal", - "filename" : "mainCellBackground.png", - "scale" : "1x" - }, - { - "resizing" : { - "mode" : "9-part", - "center" : { - "mode" : "tile", - "width" : 1, - "height" : 1 - }, - "cap-insets" : { - "bottom" : 2, - "top" : 2, - "right" : 2, - "left" : 2 - } - }, - "idiom" : "universal", - "filename" : "mainCellBackground@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/mainCellBackground.imageset/mainCellBackground.png b/TTNews/Assets.xcassets/Essence/mainCellBackground.imageset/mainCellBackground.png deleted file mode 100644 index 9b0b23e..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellBackground.imageset/mainCellBackground.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellBackground.imageset/mainCellBackground@2x.png b/TTNews/Assets.xcassets/Essence/mainCellBackground.imageset/mainCellBackground@2x.png deleted file mode 100644 index 9e501ba..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellBackground.imageset/mainCellBackground@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellCai.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/mainCellCai.imageset/Contents.json deleted file mode 100644 index d748135..0000000 --- a/TTNews/Assets.xcassets/Essence/mainCellCai.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "mainCellCai.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mainCellCai@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/mainCellCai.imageset/mainCellCai.png b/TTNews/Assets.xcassets/Essence/mainCellCai.imageset/mainCellCai.png deleted file mode 100644 index 339f81e..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellCai.imageset/mainCellCai.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellCai.imageset/mainCellCai@2x.png b/TTNews/Assets.xcassets/Essence/mainCellCai.imageset/mainCellCai@2x.png deleted file mode 100644 index fd4a5e3..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellCai.imageset/mainCellCai@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellCaiClick.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/mainCellCaiClick.imageset/Contents.json deleted file mode 100644 index 3d473a8..0000000 --- a/TTNews/Assets.xcassets/Essence/mainCellCaiClick.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "mainCellCaiClick.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mainCellCaiClick@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/mainCellCaiClick.imageset/mainCellCaiClick.png b/TTNews/Assets.xcassets/Essence/mainCellCaiClick.imageset/mainCellCaiClick.png deleted file mode 100644 index 0327a7c..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellCaiClick.imageset/mainCellCaiClick.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellCaiClick.imageset/mainCellCaiClick@2x.png b/TTNews/Assets.xcassets/Essence/mainCellCaiClick.imageset/mainCellCaiClick@2x.png deleted file mode 100644 index 8891ef5..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellCaiClick.imageset/mainCellCaiClick@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellComment.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/mainCellComment.imageset/Contents.json deleted file mode 100644 index 22e018d..0000000 --- a/TTNews/Assets.xcassets/Essence/mainCellComment.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "mainCellComment.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mainCellComment@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/mainCellComment.imageset/mainCellComment.png b/TTNews/Assets.xcassets/Essence/mainCellComment.imageset/mainCellComment.png deleted file mode 100644 index f2c494c..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellComment.imageset/mainCellComment.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellComment.imageset/mainCellComment@2x.png b/TTNews/Assets.xcassets/Essence/mainCellComment.imageset/mainCellComment@2x.png deleted file mode 100644 index fe2f578..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellComment.imageset/mainCellComment@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellCommentClick.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/mainCellCommentClick.imageset/Contents.json deleted file mode 100644 index a35c283..0000000 --- a/TTNews/Assets.xcassets/Essence/mainCellCommentClick.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "mainCellCommentClick.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mainCellCommentClick@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/mainCellCommentClick.imageset/mainCellCommentClick.png b/TTNews/Assets.xcassets/Essence/mainCellCommentClick.imageset/mainCellCommentClick.png deleted file mode 100644 index d93cfbc..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellCommentClick.imageset/mainCellCommentClick.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellCommentClick.imageset/mainCellCommentClick@2x.png b/TTNews/Assets.xcassets/Essence/mainCellCommentClick.imageset/mainCellCommentClick@2x.png deleted file mode 100644 index 3c1d938..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellCommentClick.imageset/mainCellCommentClick@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellDing.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/mainCellDing.imageset/Contents.json deleted file mode 100644 index 16ba7d0..0000000 --- a/TTNews/Assets.xcassets/Essence/mainCellDing.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "mainCellDing.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mainCellDing@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/mainCellDing.imageset/mainCellDing.png b/TTNews/Assets.xcassets/Essence/mainCellDing.imageset/mainCellDing.png deleted file mode 100644 index bfa424b..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellDing.imageset/mainCellDing.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellDing.imageset/mainCellDing@2x.png b/TTNews/Assets.xcassets/Essence/mainCellDing.imageset/mainCellDing@2x.png deleted file mode 100644 index ec48241..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellDing.imageset/mainCellDing@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellDingClick.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/mainCellDingClick.imageset/Contents.json deleted file mode 100644 index 0c5ed24..0000000 --- a/TTNews/Assets.xcassets/Essence/mainCellDingClick.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "mainCellDingClick.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mainCellDingClick@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/mainCellDingClick.imageset/mainCellDingClick.png b/TTNews/Assets.xcassets/Essence/mainCellDingClick.imageset/mainCellDingClick.png deleted file mode 100644 index c8df295..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellDingClick.imageset/mainCellDingClick.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellDingClick.imageset/mainCellDingClick@2x.png b/TTNews/Assets.xcassets/Essence/mainCellDingClick.imageset/mainCellDingClick@2x.png deleted file mode 100644 index 71e10c3..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellDingClick.imageset/mainCellDingClick@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellShare.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/mainCellShare.imageset/Contents.json deleted file mode 100644 index a472e28..0000000 --- a/TTNews/Assets.xcassets/Essence/mainCellShare.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "mainCellShare.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mainCellShare@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/mainCellShare.imageset/mainCellShare.png b/TTNews/Assets.xcassets/Essence/mainCellShare.imageset/mainCellShare.png deleted file mode 100644 index aec8a52..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellShare.imageset/mainCellShare.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellShare.imageset/mainCellShare@2x.png b/TTNews/Assets.xcassets/Essence/mainCellShare.imageset/mainCellShare@2x.png deleted file mode 100644 index de5ec36..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellShare.imageset/mainCellShare@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellShareClick.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/mainCellShareClick.imageset/Contents.json deleted file mode 100644 index 682416b..0000000 --- a/TTNews/Assets.xcassets/Essence/mainCellShareClick.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "mainCellShareClick.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mainCellShareClick@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/mainCellShareClick.imageset/mainCellShareClick.png b/TTNews/Assets.xcassets/Essence/mainCellShareClick.imageset/mainCellShareClick.png deleted file mode 100644 index 2d11cb2..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellShareClick.imageset/mainCellShareClick.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/mainCellShareClick.imageset/mainCellShareClick@2x.png b/TTNews/Assets.xcassets/Essence/mainCellShareClick.imageset/mainCellShareClick@2x.png deleted file mode 100644 index 53a4390..0000000 Binary files a/TTNews/Assets.xcassets/Essence/mainCellShareClick.imageset/mainCellShareClick@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/see-big-picture-background.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/see-big-picture-background.imageset/Contents.json deleted file mode 100644 index 534e9e8..0000000 --- a/TTNews/Assets.xcassets/Essence/see-big-picture-background.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "see-big-picture-background.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "see-big-picture-background@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/see-big-picture-background.imageset/see-big-picture-background.png b/TTNews/Assets.xcassets/Essence/see-big-picture-background.imageset/see-big-picture-background.png deleted file mode 100644 index 4d90006..0000000 Binary files a/TTNews/Assets.xcassets/Essence/see-big-picture-background.imageset/see-big-picture-background.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/see-big-picture-background.imageset/see-big-picture-background@2x.png b/TTNews/Assets.xcassets/Essence/see-big-picture-background.imageset/see-big-picture-background@2x.png deleted file mode 100644 index 6cf4f7d..0000000 Binary files a/TTNews/Assets.xcassets/Essence/see-big-picture-background.imageset/see-big-picture-background@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/Contents.json deleted file mode 100644 index fb2dffe..0000000 --- a/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "see-big-picture.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "see-big-picture@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "see-big-picture@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/see-big-picture.png b/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/see-big-picture.png deleted file mode 100644 index eab91ff..0000000 Binary files a/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/see-big-picture.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/see-big-picture@2x.png b/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/see-big-picture@2x.png deleted file mode 100644 index ee90b19..0000000 Binary files a/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/see-big-picture@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/see-big-picture@3x.png b/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/see-big-picture@3x.png deleted file mode 100644 index 70c56a7..0000000 Binary files a/TTNews/Assets.xcassets/Essence/see-big-picture.imageset/see-big-picture@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/Contents.json deleted file mode 100644 index a70c2c4..0000000 --- a/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "show_image_back_icon.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "show_image_back_icon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "show_image_back_icon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/show_image_back_icon.png b/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/show_image_back_icon.png deleted file mode 100644 index 11c50a1..0000000 Binary files a/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/show_image_back_icon.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/show_image_back_icon@2x.png b/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/show_image_back_icon@2x.png deleted file mode 100644 index 95df396..0000000 Binary files a/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/show_image_back_icon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/show_image_back_icon@3x.png b/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/show_image_back_icon@3x.png deleted file mode 100644 index 2c5c689..0000000 Binary files a/TTNews/Assets.xcassets/Essence/show_image_back_icon.imageset/show_image_back_icon@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/video-play.imageset/Contents.json b/TTNews/Assets.xcassets/Essence/video-play.imageset/Contents.json deleted file mode 100644 index 3012b71..0000000 --- a/TTNews/Assets.xcassets/Essence/video-play.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "video-play.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "video-play@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "video-play@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Essence/video-play.imageset/video-play.png b/TTNews/Assets.xcassets/Essence/video-play.imageset/video-play.png deleted file mode 100644 index bfffd3b..0000000 Binary files a/TTNews/Assets.xcassets/Essence/video-play.imageset/video-play.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/video-play.imageset/video-play@2x.png b/TTNews/Assets.xcassets/Essence/video-play.imageset/video-play@2x.png deleted file mode 100644 index d1b4821..0000000 Binary files a/TTNews/Assets.xcassets/Essence/video-play.imageset/video-play@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Essence/video-play.imageset/video-play@3x.png b/TTNews/Assets.xcassets/Essence/video-play.imageset/video-play@3x.png deleted file mode 100644 index 08fd0af..0000000 Binary files a/TTNews/Assets.xcassets/Essence/video-play.imageset/video-play@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/Contents.json b/TTNews/Assets.xcassets/Play/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/TTNews/Assets.xcassets/Play/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Play/play-voice-bg-select.imageset/Contents.json b/TTNews/Assets.xcassets/Play/play-voice-bg-select.imageset/Contents.json deleted file mode 100644 index 6f0a94d..0000000 --- a/TTNews/Assets.xcassets/Play/play-voice-bg-select.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "play-voice-bg-select.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Play/play-voice-bg-select.imageset/play-voice-bg-select.png b/TTNews/Assets.xcassets/Play/play-voice-bg-select.imageset/play-voice-bg-select.png deleted file mode 100644 index d34979a..0000000 Binary files a/TTNews/Assets.xcassets/Play/play-voice-bg-select.imageset/play-voice-bg-select.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/Contents.json b/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/Contents.json deleted file mode 100644 index cbac0ba..0000000 --- a/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "play-voice-bg.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "play-voice-bg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "play-voice-bg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/play-voice-bg.png b/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/play-voice-bg.png deleted file mode 100644 index 1e816a7..0000000 Binary files a/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/play-voice-bg.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/play-voice-bg@2x.png b/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/play-voice-bg@2x.png deleted file mode 100644 index 0a24861..0000000 Binary files a/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/play-voice-bg@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/play-voice-bg@3x.png b/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/play-voice-bg@3x.png deleted file mode 100644 index 8df6123..0000000 Binary files a/TTNews/Assets.xcassets/Play/play-voice-bg.imageset/play-voice-bg@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/Contents.json b/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/Contents.json deleted file mode 100644 index db629f5..0000000 --- a/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "play-voice-stop.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "play-voice-stop@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "play-voice-stop@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/play-voice-stop.png b/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/play-voice-stop.png deleted file mode 100644 index f4c7bbd..0000000 Binary files a/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/play-voice-stop.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/play-voice-stop@2x.png b/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/play-voice-stop@2x.png deleted file mode 100644 index 9267d01..0000000 Binary files a/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/play-voice-stop@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/play-voice-stop@3x.png b/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/play-voice-stop@3x.png deleted file mode 100644 index 0f0dfc6..0000000 Binary files a/TTNews/Assets.xcassets/Play/play-voice-stop.imageset/play-voice-stop@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/play.imageset/Contents.json b/TTNews/Assets.xcassets/Play/play.imageset/Contents.json deleted file mode 100644 index db7ffa0..0000000 --- a/TTNews/Assets.xcassets/Play/play.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "play.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "play@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "play@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Play/play.imageset/play.png b/TTNews/Assets.xcassets/Play/play.imageset/play.png deleted file mode 100644 index 720645e..0000000 Binary files a/TTNews/Assets.xcassets/Play/play.imageset/play.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/play.imageset/play@2x.png b/TTNews/Assets.xcassets/Play/play.imageset/play@2x.png deleted file mode 100644 index a114ac2..0000000 Binary files a/TTNews/Assets.xcassets/Play/play.imageset/play@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/play.imageset/play@3x.png b/TTNews/Assets.xcassets/Play/play.imageset/play@3x.png deleted file mode 100644 index 0e0ae6e..0000000 Binary files a/TTNews/Assets.xcassets/Play/play.imageset/play@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/player_embeddedscreen.imageset/Contents.json b/TTNews/Assets.xcassets/Play/player_embeddedscreen.imageset/Contents.json deleted file mode 100644 index de5111a..0000000 --- a/TTNews/Assets.xcassets/Play/player_embeddedscreen.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "player_embeddedscreen@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Play/player_embeddedscreen.imageset/player_embeddedscreen@2x.png b/TTNews/Assets.xcassets/Play/player_embeddedscreen.imageset/player_embeddedscreen@2x.png deleted file mode 100644 index 83d8962..0000000 Binary files a/TTNews/Assets.xcassets/Play/player_embeddedscreen.imageset/player_embeddedscreen@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/Play/player_fullscreen.imageset/Contents.json b/TTNews/Assets.xcassets/Play/player_fullscreen.imageset/Contents.json deleted file mode 100644 index 6b84230..0000000 --- a/TTNews/Assets.xcassets/Play/player_fullscreen.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "player_fullscreen@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/Play/player_fullscreen.imageset/player_fullscreen@2x.png b/TTNews/Assets.xcassets/Play/player_fullscreen.imageset/player_fullscreen@2x.png deleted file mode 100644 index da427fd..0000000 Binary files a/TTNews/Assets.xcassets/Play/player_fullscreen.imageset/player_fullscreen@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/Contents.json b/TTNews/Assets.xcassets/navigationbar/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/TTNews/Assets.xcassets/navigationbar/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_normal.imageset/Contents.json b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_normal.imageset/Contents.json deleted file mode 100644 index e6bcb4f..0000000 --- a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_normal.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "navigationBarItem_favorite_normal@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "navigationBarItem_favorite_normal@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_normal.imageset/navigationBarItem_favorite_normal@2x.png b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_normal.imageset/navigationBarItem_favorite_normal@2x.png deleted file mode 100644 index 9853761..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_normal.imageset/navigationBarItem_favorite_normal@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_normal.imageset/navigationBarItem_favorite_normal@3x.png b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_normal.imageset/navigationBarItem_favorite_normal@3x.png deleted file mode 100644 index 44e95b0..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_normal.imageset/navigationBarItem_favorite_normal@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_pressed.imageset/Contents.json b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_pressed.imageset/Contents.json deleted file mode 100644 index 1d913e3..0000000 --- a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_pressed.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "navigationBarItem_favorite_pressed@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "navigationBarItem_favorite_pressed@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_pressed.imageset/navigationBarItem_favorite_pressed@2x.png b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_pressed.imageset/navigationBarItem_favorite_pressed@2x.png deleted file mode 100644 index ef7e98f..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_pressed.imageset/navigationBarItem_favorite_pressed@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_pressed.imageset/navigationBarItem_favorite_pressed@3x.png b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_pressed.imageset/navigationBarItem_favorite_pressed@3x.png deleted file mode 100644 index a77ed0d..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorite_pressed.imageset/navigationBarItem_favorite_pressed@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_normal.imageset/Contents.json b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_normal.imageset/Contents.json deleted file mode 100644 index 38849e6..0000000 --- a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_normal.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "navigationBarItem_favorited_normal@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "navigationBarItem_favorited_normal@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_normal.imageset/navigationBarItem_favorited_normal@2x.png b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_normal.imageset/navigationBarItem_favorited_normal@2x.png deleted file mode 100644 index 8a797f6..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_normal.imageset/navigationBarItem_favorited_normal@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_normal.imageset/navigationBarItem_favorited_normal@3x.png b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_normal.imageset/navigationBarItem_favorited_normal@3x.png deleted file mode 100644 index 4bb7544..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_normal.imageset/navigationBarItem_favorited_normal@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_pressed.imageset/Contents.json b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_pressed.imageset/Contents.json deleted file mode 100644 index aeb0cb6..0000000 --- a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_pressed.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "navigationBarItem_favorited_pressed@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "navigationBarItem_favorited_pressed@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_pressed.imageset/navigationBarItem_favorited_pressed@2x.png b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_pressed.imageset/navigationBarItem_favorited_pressed@2x.png deleted file mode 100644 index 2684886..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_pressed.imageset/navigationBarItem_favorited_pressed@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_pressed.imageset/navigationBarItem_favorited_pressed@3x.png b/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_pressed.imageset/navigationBarItem_favorited_pressed@3x.png deleted file mode 100644 index 89dd07b..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationBarItem_favorited_pressed.imageset/navigationBarItem_favorited_pressed@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationbar_back_icon.imageset/Contents.json b/TTNews/Assets.xcassets/navigationbar/navigationbar_back_icon.imageset/Contents.json deleted file mode 100644 index a89bc52..0000000 --- a/TTNews/Assets.xcassets/navigationbar/navigationbar_back_icon.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "navigationbar_back_icon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "navigationbar_back_icon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/navigationbar/navigationbar_back_icon.imageset/navigationbar_back_icon@2x.png b/TTNews/Assets.xcassets/navigationbar/navigationbar_back_icon.imageset/navigationbar_back_icon@2x.png deleted file mode 100644 index e2031f5..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationbar_back_icon.imageset/navigationbar_back_icon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationbar_back_icon.imageset/navigationbar_back_icon@3x.png b/TTNews/Assets.xcassets/navigationbar/navigationbar_back_icon.imageset/navigationbar_back_icon@3x.png deleted file mode 100644 index 3c668a3..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationbar_back_icon.imageset/navigationbar_back_icon@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationbar_pic_back_icon.imageset/Contents.json b/TTNews/Assets.xcassets/navigationbar/navigationbar_pic_back_icon.imageset/Contents.json deleted file mode 100644 index 17ce41b..0000000 --- a/TTNews/Assets.xcassets/navigationbar/navigationbar_pic_back_icon.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "navigationbar_pic_back_icon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "navigationbar_pic_back_icon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/navigationbar/navigationbar_pic_back_icon.imageset/navigationbar_pic_back_icon@2x.png b/TTNews/Assets.xcassets/navigationbar/navigationbar_pic_back_icon.imageset/navigationbar_pic_back_icon@2x.png deleted file mode 100644 index b0181cd..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationbar_pic_back_icon.imageset/navigationbar_pic_back_icon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/navigationbar_pic_back_icon.imageset/navigationbar_pic_back_icon@3x.png b/TTNews/Assets.xcassets/navigationbar/navigationbar_pic_back_icon.imageset/navigationbar_pic_back_icon@3x.png deleted file mode 100644 index 293c11a..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/navigationbar_pic_back_icon.imageset/navigationbar_pic_back_icon@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/toolbar_back_icon.imageset/Contents.json b/TTNews/Assets.xcassets/navigationbar/toolbar_back_icon.imageset/Contents.json deleted file mode 100644 index 92f3038..0000000 --- a/TTNews/Assets.xcassets/navigationbar/toolbar_back_icon.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "toolbar_back_icon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tool_back_icon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/navigationbar/toolbar_back_icon.imageset/tool_back_icon@3x.png b/TTNews/Assets.xcassets/navigationbar/toolbar_back_icon.imageset/tool_back_icon@3x.png deleted file mode 100644 index 3c668a3..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/toolbar_back_icon.imageset/tool_back_icon@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/toolbar_back_icon.imageset/toolbar_back_icon@2x.png b/TTNews/Assets.xcassets/navigationbar/toolbar_back_icon.imageset/toolbar_back_icon@2x.png deleted file mode 100644 index e2031f5..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/toolbar_back_icon.imageset/toolbar_back_icon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/toolbar_forward_icon.imageset/Contents.json b/TTNews/Assets.xcassets/navigationbar/toolbar_forward_icon.imageset/Contents.json deleted file mode 100644 index 3690b50..0000000 --- a/TTNews/Assets.xcassets/navigationbar/toolbar_forward_icon.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "toolbar_forward_icon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tool_forward_icon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/navigationbar/toolbar_forward_icon.imageset/tool_forward_icon@3x.png b/TTNews/Assets.xcassets/navigationbar/toolbar_forward_icon.imageset/tool_forward_icon@3x.png deleted file mode 100644 index 060b052..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/toolbar_forward_icon.imageset/tool_forward_icon@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/navigationbar/toolbar_forward_icon.imageset/toolbar_forward_icon@2x.png b/TTNews/Assets.xcassets/navigationbar/toolbar_forward_icon.imageset/toolbar_forward_icon@2x.png deleted file mode 100644 index 7c445d3..0000000 Binary files a/TTNews/Assets.xcassets/navigationbar/toolbar_forward_icon.imageset/toolbar_forward_icon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/night_photoset_list_cell_icon-1.imageset/Contents.json b/TTNews/Assets.xcassets/night_photoset_list_cell_icon-1.imageset/Contents.json deleted file mode 100644 index de8a461..0000000 --- a/TTNews/Assets.xcassets/night_photoset_list_cell_icon-1.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "night_photoset_list_cell_icon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/night_photoset_list_cell_icon-1.imageset/night_photoset_list_cell_icon@2x.png b/TTNews/Assets.xcassets/night_photoset_list_cell_icon-1.imageset/night_photoset_list_cell_icon@2x.png deleted file mode 100644 index 9186649..0000000 Binary files a/TTNews/Assets.xcassets/night_photoset_list_cell_icon-1.imageset/night_photoset_list_cell_icon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/night_photoset_list_cell_icon-2.imageset/Contents.json b/TTNews/Assets.xcassets/night_photoset_list_cell_icon-2.imageset/Contents.json deleted file mode 100644 index de8a461..0000000 --- a/TTNews/Assets.xcassets/night_photoset_list_cell_icon-2.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "night_photoset_list_cell_icon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/night_photoset_list_cell_icon-2.imageset/night_photoset_list_cell_icon@2x.png b/TTNews/Assets.xcassets/night_photoset_list_cell_icon-2.imageset/night_photoset_list_cell_icon@2x.png deleted file mode 100644 index 9186649..0000000 Binary files a/TTNews/Assets.xcassets/night_photoset_list_cell_icon-2.imageset/night_photoset_list_cell_icon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/night_photoset_list_cell_icon.imageset/Contents.json b/TTNews/Assets.xcassets/night_photoset_list_cell_icon.imageset/Contents.json deleted file mode 100644 index de8a461..0000000 --- a/TTNews/Assets.xcassets/night_photoset_list_cell_icon.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "night_photoset_list_cell_icon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/night_photoset_list_cell_icon.imageset/night_photoset_list_cell_icon@2x.png b/TTNews/Assets.xcassets/night_photoset_list_cell_icon.imageset/night_photoset_list_cell_icon@2x.png deleted file mode 100644 index 9186649..0000000 Binary files a/TTNews/Assets.xcassets/night_photoset_list_cell_icon.imageset/night_photoset_list_cell_icon@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/Contents.json b/TTNews/Assets.xcassets/other/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/TTNews/Assets.xcassets/other/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/KBSkinToolbar_icon_hide.imageset/Contents.json b/TTNews/Assets.xcassets/other/KBSkinToolbar_icon_hide.imageset/Contents.json deleted file mode 100644 index 7d0fc97..0000000 --- a/TTNews/Assets.xcassets/other/KBSkinToolbar_icon_hide.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "KBSkinToolbar_icon_hide@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "KBSkinToolbar_icon_hide@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/KBSkinToolbar_icon_hide.imageset/KBSkinToolbar_icon_hide@2x.png b/TTNews/Assets.xcassets/other/KBSkinToolbar_icon_hide.imageset/KBSkinToolbar_icon_hide@2x.png deleted file mode 100644 index 0f3249c..0000000 Binary files a/TTNews/Assets.xcassets/other/KBSkinToolbar_icon_hide.imageset/KBSkinToolbar_icon_hide@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/KBSkinToolbar_icon_hide.imageset/KBSkinToolbar_icon_hide@3x.png b/TTNews/Assets.xcassets/other/KBSkinToolbar_icon_hide.imageset/KBSkinToolbar_icon_hide@3x.png deleted file mode 100644 index c1487d7..0000000 Binary files a/TTNews/Assets.xcassets/other/KBSkinToolbar_icon_hide.imageset/KBSkinToolbar_icon_hide@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/MaximumTrackImage.imageset/Contents.json b/TTNews/Assets.xcassets/other/MaximumTrackImage.imageset/Contents.json deleted file mode 100644 index bc412be..0000000 --- a/TTNews/Assets.xcassets/other/MaximumTrackImage.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "MaximumTrackImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/MaximumTrackImage.imageset/MaximumTrackImage@2x.png b/TTNews/Assets.xcassets/other/MaximumTrackImage.imageset/MaximumTrackImage@2x.png deleted file mode 100644 index 5873fe2..0000000 Binary files a/TTNews/Assets.xcassets/other/MaximumTrackImage.imageset/MaximumTrackImage@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/MinimumTrackImage.imageset/Contents.json b/TTNews/Assets.xcassets/other/MinimumTrackImage.imageset/Contents.json deleted file mode 100644 index 0a0a208..0000000 --- a/TTNews/Assets.xcassets/other/MinimumTrackImage.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "MinimumTrackImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/MinimumTrackImage.imageset/MinimumTrackImage@2x.png b/TTNews/Assets.xcassets/other/MinimumTrackImage.imageset/MinimumTrackImage@2x.png deleted file mode 100644 index 3dff068..0000000 Binary files a/TTNews/Assets.xcassets/other/MinimumTrackImage.imageset/MinimumTrackImage@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/appinfoimage.imageset/Contents.json b/TTNews/Assets.xcassets/other/appinfoimage.imageset/Contents.json deleted file mode 100644 index f6ef8d2..0000000 --- a/TTNews/Assets.xcassets/other/appinfoimage.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "appinfoimage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/appinfoimage.imageset/appinfoimage.png b/TTNews/Assets.xcassets/other/appinfoimage.imageset/appinfoimage.png deleted file mode 100644 index a9c720a..0000000 Binary files a/TTNews/Assets.xcassets/other/appinfoimage.imageset/appinfoimage.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/bg_media_default.imageset/Contents.json b/TTNews/Assets.xcassets/other/bg_media_default.imageset/Contents.json deleted file mode 100644 index 1c9b065..0000000 --- a/TTNews/Assets.xcassets/other/bg_media_default.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bg_media_default@2x.jpg", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/bg_media_default.imageset/bg_media_default@2x.jpg b/TTNews/Assets.xcassets/other/bg_media_default.imageset/bg_media_default@2x.jpg deleted file mode 100644 index 0d8d327..0000000 Binary files a/TTNews/Assets.xcassets/other/bg_media_default.imageset/bg_media_default@2x.jpg and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/card_add.imageset/Contents.json b/TTNews/Assets.xcassets/other/card_add.imageset/Contents.json deleted file mode 100644 index a1d083c..0000000 --- a/TTNews/Assets.xcassets/other/card_add.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "card_add@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "card_add@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/card_add.imageset/card_add@2x.png b/TTNews/Assets.xcassets/other/card_add.imageset/card_add@2x.png deleted file mode 100644 index d9e98c4..0000000 Binary files a/TTNews/Assets.xcassets/other/card_add.imageset/card_add@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/card_add.imageset/card_add@3x.png b/TTNews/Assets.xcassets/other/card_add.imageset/card_add@3x.png deleted file mode 100644 index f9d64b4..0000000 Binary files a/TTNews/Assets.xcassets/other/card_add.imageset/card_add@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/cellmorebtnclick.imageset/Contents.json b/TTNews/Assets.xcassets/other/cellmorebtnclick.imageset/Contents.json deleted file mode 100644 index 48b3cb4..0000000 --- a/TTNews/Assets.xcassets/other/cellmorebtnclick.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "cellmorebtnclick.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cellmorebtnclick@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/cellmorebtnclick.imageset/cellmorebtnclick.png b/TTNews/Assets.xcassets/other/cellmorebtnclick.imageset/cellmorebtnclick.png deleted file mode 100644 index dec7ba4..0000000 Binary files a/TTNews/Assets.xcassets/other/cellmorebtnclick.imageset/cellmorebtnclick.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/cellmorebtnclick.imageset/cellmorebtnclick@2x.png b/TTNews/Assets.xcassets/other/cellmorebtnclick.imageset/cellmorebtnclick@2x.png deleted file mode 100644 index f4474cf..0000000 Binary files a/TTNews/Assets.xcassets/other/cellmorebtnclick.imageset/cellmorebtnclick@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/cellmorebtnnormal.imageset/Contents.json b/TTNews/Assets.xcassets/other/cellmorebtnnormal.imageset/Contents.json deleted file mode 100644 index ef4e880..0000000 --- a/TTNews/Assets.xcassets/other/cellmorebtnnormal.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "cellmorebtnnormal.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cellmorebtnnormal@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/cellmorebtnnormal.imageset/cellmorebtnnormal.png b/TTNews/Assets.xcassets/other/cellmorebtnnormal.imageset/cellmorebtnnormal.png deleted file mode 100644 index b5b5045..0000000 Binary files a/TTNews/Assets.xcassets/other/cellmorebtnnormal.imageset/cellmorebtnnormal.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/cellmorebtnnormal.imageset/cellmorebtnnormal@2x.png b/TTNews/Assets.xcassets/other/cellmorebtnnormal.imageset/cellmorebtnnormal@2x.png deleted file mode 100644 index a382af8..0000000 Binary files a/TTNews/Assets.xcassets/other/cellmorebtnnormal.imageset/cellmorebtnnormal@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/channel_edit_delete.imageset/Contents.json b/TTNews/Assets.xcassets/other/channel_edit_delete.imageset/Contents.json deleted file mode 100644 index abd5d1f..0000000 --- a/TTNews/Assets.xcassets/other/channel_edit_delete.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "channel_edit_delete@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/channel_edit_delete.imageset/channel_edit_delete@2x.png b/TTNews/Assets.xcassets/other/channel_edit_delete.imageset/channel_edit_delete@2x.png deleted file mode 100644 index 4877ffc..0000000 Binary files a/TTNews/Assets.xcassets/other/channel_edit_delete.imageset/channel_edit_delete@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/feed_focus_newstype_pic.imageset/Contents.json b/TTNews/Assets.xcassets/other/feed_focus_newstype_pic.imageset/Contents.json deleted file mode 100644 index 2175cdb..0000000 --- a/TTNews/Assets.xcassets/other/feed_focus_newstype_pic.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "feed_focus_newstype_pic@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "feed_focus_newstype_pic@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/feed_focus_newstype_pic.imageset/feed_focus_newstype_pic@2x.png b/TTNews/Assets.xcassets/other/feed_focus_newstype_pic.imageset/feed_focus_newstype_pic@2x.png deleted file mode 100644 index e4479ed..0000000 Binary files a/TTNews/Assets.xcassets/other/feed_focus_newstype_pic.imageset/feed_focus_newstype_pic@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/feed_focus_newstype_pic.imageset/feed_focus_newstype_pic@3x.png b/TTNews/Assets.xcassets/other/feed_focus_newstype_pic.imageset/feed_focus_newstype_pic@3x.png deleted file mode 100644 index 85bbb6c..0000000 Binary files a/TTNews/Assets.xcassets/other/feed_focus_newstype_pic.imageset/feed_focus_newstype_pic@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/full_minimize_btn_hl.imageset/Contents.json b/TTNews/Assets.xcassets/other/full_minimize_btn_hl.imageset/Contents.json deleted file mode 100644 index 5b3368d..0000000 --- a/TTNews/Assets.xcassets/other/full_minimize_btn_hl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "full_minimize_btn_hl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "full_minimize_btn_hl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/full_minimize_btn_hl.imageset/full_minimize_btn_hl@2x.png b/TTNews/Assets.xcassets/other/full_minimize_btn_hl.imageset/full_minimize_btn_hl@2x.png deleted file mode 100644 index 93f5b03..0000000 Binary files a/TTNews/Assets.xcassets/other/full_minimize_btn_hl.imageset/full_minimize_btn_hl@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/full_minimize_btn_hl.imageset/full_minimize_btn_hl@3x.png b/TTNews/Assets.xcassets/other/full_minimize_btn_hl.imageset/full_minimize_btn_hl@3x.png deleted file mode 100644 index 4f57b06..0000000 Binary files a/TTNews/Assets.xcassets/other/full_minimize_btn_hl.imageset/full_minimize_btn_hl@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/full_pause_btn_hl.imageset/Contents.json b/TTNews/Assets.xcassets/other/full_pause_btn_hl.imageset/Contents.json deleted file mode 100644 index 9a8cba1..0000000 --- a/TTNews/Assets.xcassets/other/full_pause_btn_hl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "full_pause_btn_hl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "full_pause_btn_hl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/full_pause_btn_hl.imageset/full_pause_btn_hl@2x.png b/TTNews/Assets.xcassets/other/full_pause_btn_hl.imageset/full_pause_btn_hl@2x.png deleted file mode 100644 index 9dfd53e..0000000 Binary files a/TTNews/Assets.xcassets/other/full_pause_btn_hl.imageset/full_pause_btn_hl@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/full_pause_btn_hl.imageset/full_pause_btn_hl@3x.png b/TTNews/Assets.xcassets/other/full_pause_btn_hl.imageset/full_pause_btn_hl@3x.png deleted file mode 100644 index 8b773ac..0000000 Binary files a/TTNews/Assets.xcassets/other/full_pause_btn_hl.imageset/full_pause_btn_hl@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/full_play_btn_hl.imageset/Contents.json b/TTNews/Assets.xcassets/other/full_play_btn_hl.imageset/Contents.json deleted file mode 100644 index 21bbacb..0000000 --- a/TTNews/Assets.xcassets/other/full_play_btn_hl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "full_play_btn_hl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "full_play_btn_hl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/full_play_btn_hl.imageset/full_play_btn_hl@2x.png b/TTNews/Assets.xcassets/other/full_play_btn_hl.imageset/full_play_btn_hl@2x.png deleted file mode 100644 index 6fbac2c..0000000 Binary files a/TTNews/Assets.xcassets/other/full_play_btn_hl.imageset/full_play_btn_hl@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/full_play_btn_hl.imageset/full_play_btn_hl@3x.png b/TTNews/Assets.xcassets/other/full_play_btn_hl.imageset/full_play_btn_hl@3x.png deleted file mode 100644 index a391975..0000000 Binary files a/TTNews/Assets.xcassets/other/full_play_btn_hl.imageset/full_play_btn_hl@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/home_header_add.imageset/Contents.json b/TTNews/Assets.xcassets/other/home_header_add.imageset/Contents.json deleted file mode 100644 index 97daf5e..0000000 --- a/TTNews/Assets.xcassets/other/home_header_add.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "home_header_add@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/home_header_add.imageset/home_header_add@2x.png b/TTNews/Assets.xcassets/other/home_header_add.imageset/home_header_add@2x.png deleted file mode 100644 index 416381d..0000000 Binary files a/TTNews/Assets.xcassets/other/home_header_add.imageset/home_header_add@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/home_header_add_slim.imageset/Contents.json b/TTNews/Assets.xcassets/other/home_header_add_slim.imageset/Contents.json deleted file mode 100644 index 3358fba..0000000 --- a/TTNews/Assets.xcassets/other/home_header_add_slim.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "home_header_add_slim@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/home_header_add_slim.imageset/home_header_add_slim@2x.png b/TTNews/Assets.xcassets/other/home_header_add_slim.imageset/home_header_add_slim@2x.png deleted file mode 100644 index d858475..0000000 Binary files a/TTNews/Assets.xcassets/other/home_header_add_slim.imageset/home_header_add_slim@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/mini_launchFullScreen_btn_hl.imageset/Contents.json b/TTNews/Assets.xcassets/other/mini_launchFullScreen_btn_hl.imageset/Contents.json deleted file mode 100644 index 1e61788..0000000 --- a/TTNews/Assets.xcassets/other/mini_launchFullScreen_btn_hl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mini_launchFullScreen_btn_hl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mini_launchFullScreen_btn_hl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/mini_launchFullScreen_btn_hl.imageset/mini_launchFullScreen_btn_hl@2x.png b/TTNews/Assets.xcassets/other/mini_launchFullScreen_btn_hl.imageset/mini_launchFullScreen_btn_hl@2x.png deleted file mode 100644 index a406089..0000000 Binary files a/TTNews/Assets.xcassets/other/mini_launchFullScreen_btn_hl.imageset/mini_launchFullScreen_btn_hl@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/mini_launchFullScreen_btn_hl.imageset/mini_launchFullScreen_btn_hl@3x.png b/TTNews/Assets.xcassets/other/mini_launchFullScreen_btn_hl.imageset/mini_launchFullScreen_btn_hl@3x.png deleted file mode 100644 index 9fbbcf6..0000000 Binary files a/TTNews/Assets.xcassets/other/mini_launchFullScreen_btn_hl.imageset/mini_launchFullScreen_btn_hl@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/news_place.imageset/Contents.json b/TTNews/Assets.xcassets/other/news_place.imageset/Contents.json deleted file mode 100644 index 6acd8d1..0000000 --- a/TTNews/Assets.xcassets/other/news_place.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "news_place@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "news_place@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/news_place.imageset/news_place@2x.png b/TTNews/Assets.xcassets/other/news_place.imageset/news_place@2x.png deleted file mode 100644 index 7a0f7bc..0000000 Binary files a/TTNews/Assets.xcassets/other/news_place.imageset/news_place@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/news_place.imageset/news_place@3x.png b/TTNews/Assets.xcassets/other/news_place.imageset/news_place@3x.png deleted file mode 100644 index 337f1ce..0000000 Binary files a/TTNews/Assets.xcassets/other/news_place.imageset/news_place@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/night_specialcell_nav_btn.imageset/Contents.json b/TTNews/Assets.xcassets/other/night_specialcell_nav_btn.imageset/Contents.json deleted file mode 100644 index 9a28853..0000000 --- a/TTNews/Assets.xcassets/other/night_specialcell_nav_btn.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "night_specialcell_nav_btn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/night_specialcell_nav_btn.imageset/night_specialcell_nav_btn@2x.png b/TTNews/Assets.xcassets/other/night_specialcell_nav_btn.imageset/night_specialcell_nav_btn@2x.png deleted file mode 100644 index 95a628d..0000000 Binary files a/TTNews/Assets.xcassets/other/night_specialcell_nav_btn.imageset/night_specialcell_nav_btn@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/slidetab_mask.imageset/Contents.json b/TTNews/Assets.xcassets/other/slidetab_mask.imageset/Contents.json deleted file mode 100644 index 064d33f..0000000 --- a/TTNews/Assets.xcassets/other/slidetab_mask.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "slidetab_mask@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "slidetab_mask@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/slidetab_mask.imageset/slidetab_mask@2x.png b/TTNews/Assets.xcassets/other/slidetab_mask.imageset/slidetab_mask@2x.png deleted file mode 100644 index 2137c92..0000000 Binary files a/TTNews/Assets.xcassets/other/slidetab_mask.imageset/slidetab_mask@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/slidetab_mask.imageset/slidetab_mask@3x.png b/TTNews/Assets.xcassets/other/slidetab_mask.imageset/slidetab_mask@3x.png deleted file mode 100644 index 1480e78..0000000 Binary files a/TTNews/Assets.xcassets/other/slidetab_mask.imageset/slidetab_mask@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/specialcell_nav_btn.imageset/Contents.json b/TTNews/Assets.xcassets/other/specialcell_nav_btn.imageset/Contents.json deleted file mode 100644 index a0bc213..0000000 --- a/TTNews/Assets.xcassets/other/specialcell_nav_btn.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "specialcell_nav_btn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/specialcell_nav_btn.imageset/specialcell_nav_btn@2x.png b/TTNews/Assets.xcassets/other/specialcell_nav_btn.imageset/specialcell_nav_btn@2x.png deleted file mode 100644 index ab70cf3..0000000 Binary files a/TTNews/Assets.xcassets/other/specialcell_nav_btn.imageset/specialcell_nav_btn@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/other/thumbImage.imageset/Contents.json b/TTNews/Assets.xcassets/other/thumbImage.imageset/Contents.json deleted file mode 100644 index 103f5b3..0000000 --- a/TTNews/Assets.xcassets/other/thumbImage.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "thumbImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/other/thumbImage.imageset/thumbImage@2x.png b/TTNews/Assets.xcassets/other/thumbImage.imageset/thumbImage@2x.png deleted file mode 100644 index b282be3..0000000 Binary files a/TTNews/Assets.xcassets/other/thumbImage.imageset/thumbImage@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/Contents.json b/TTNews/Assets.xcassets/tabbar/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/TTNews/Assets.xcassets/tabbar/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_news.imageset/Contents.json b/TTNews/Assets.xcassets/tabbar/tabbar_news.imageset/Contents.json deleted file mode 100644 index 9ccb839..0000000 --- a/TTNews/Assets.xcassets/tabbar/tabbar_news.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_news@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_news@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_news.imageset/tabbar_news@2x.png b/TTNews/Assets.xcassets/tabbar/tabbar_news.imageset/tabbar_news@2x.png deleted file mode 100644 index 33dd5f0..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_news.imageset/tabbar_news@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_news.imageset/tabbar_news@3x.png b/TTNews/Assets.xcassets/tabbar/tabbar_news.imageset/tabbar_news@3x.png deleted file mode 100644 index 355716a..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_news.imageset/tabbar_news@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_news_hl.imageset/Contents.json b/TTNews/Assets.xcassets/tabbar/tabbar_news_hl.imageset/Contents.json deleted file mode 100644 index 0952df9..0000000 --- a/TTNews/Assets.xcassets/tabbar/tabbar_news_hl.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_news_hl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_news_hl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_news_hl.imageset/tabbar_news_hl@2x.png b/TTNews/Assets.xcassets/tabbar/tabbar_news_hl.imageset/tabbar_news_hl@2x.png deleted file mode 100644 index e3ba345..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_news_hl.imageset/tabbar_news_hl@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_news_hl.imageset/tabbar_news_hl@3x.png b/TTNews/Assets.xcassets/tabbar/tabbar_news_hl.imageset/tabbar_news_hl@3x.png deleted file mode 100644 index 1ddce82..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_news_hl.imageset/tabbar_news_hl@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_picture.imageset/Contents.json b/TTNews/Assets.xcassets/tabbar/tabbar_picture.imageset/Contents.json deleted file mode 100644 index a79e97b..0000000 --- a/TTNews/Assets.xcassets/tabbar/tabbar_picture.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_picture@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_picture@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_picture.imageset/tabbar_picture@2x.png b/TTNews/Assets.xcassets/tabbar/tabbar_picture.imageset/tabbar_picture@2x.png deleted file mode 100644 index b75e4bf..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_picture.imageset/tabbar_picture@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_picture.imageset/tabbar_picture@3x.png b/TTNews/Assets.xcassets/tabbar/tabbar_picture.imageset/tabbar_picture@3x.png deleted file mode 100644 index d4a92ac..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_picture.imageset/tabbar_picture@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_picture_hl.imageset/Contents.json b/TTNews/Assets.xcassets/tabbar/tabbar_picture_hl.imageset/Contents.json deleted file mode 100644 index f1dc1cc..0000000 --- a/TTNews/Assets.xcassets/tabbar/tabbar_picture_hl.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_picture_hl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_picture_hl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_picture_hl.imageset/tabbar_picture_hl@2x.png b/TTNews/Assets.xcassets/tabbar/tabbar_picture_hl.imageset/tabbar_picture_hl@2x.png deleted file mode 100644 index 4e3768a..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_picture_hl.imageset/tabbar_picture_hl@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_picture_hl.imageset/tabbar_picture_hl@3x.png b/TTNews/Assets.xcassets/tabbar/tabbar_picture_hl.imageset/tabbar_picture_hl@3x.png deleted file mode 100644 index 39c9996..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_picture_hl.imageset/tabbar_picture_hl@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_setting.imageset/Contents.json b/TTNews/Assets.xcassets/tabbar/tabbar_setting.imageset/Contents.json deleted file mode 100644 index 4007067..0000000 --- a/TTNews/Assets.xcassets/tabbar/tabbar_setting.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_setting@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_setting@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_setting.imageset/tabbar_setting@2x.png b/TTNews/Assets.xcassets/tabbar/tabbar_setting.imageset/tabbar_setting@2x.png deleted file mode 100644 index 2b08dcd..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_setting.imageset/tabbar_setting@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_setting.imageset/tabbar_setting@3x.png b/TTNews/Assets.xcassets/tabbar/tabbar_setting.imageset/tabbar_setting@3x.png deleted file mode 100644 index dbd419a..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_setting.imageset/tabbar_setting@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_setting_hl.imageset/Contents.json b/TTNews/Assets.xcassets/tabbar/tabbar_setting_hl.imageset/Contents.json deleted file mode 100644 index c919567..0000000 --- a/TTNews/Assets.xcassets/tabbar/tabbar_setting_hl.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_setting_hl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_setting_hl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_setting_hl.imageset/tabbar_setting_hl@2x.png b/TTNews/Assets.xcassets/tabbar/tabbar_setting_hl.imageset/tabbar_setting_hl@2x.png deleted file mode 100644 index 0c7c16b..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_setting_hl.imageset/tabbar_setting_hl@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_setting_hl.imageset/tabbar_setting_hl@3x.png b/TTNews/Assets.xcassets/tabbar/tabbar_setting_hl.imageset/tabbar_setting_hl@3x.png deleted file mode 100644 index bab44e5..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_setting_hl.imageset/tabbar_setting_hl@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_video.imageset/Contents.json b/TTNews/Assets.xcassets/tabbar/tabbar_video.imageset/Contents.json deleted file mode 100644 index 9c0b65f..0000000 --- a/TTNews/Assets.xcassets/tabbar/tabbar_video.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_video@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_video@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_video.imageset/tabbar_video@2x.png b/TTNews/Assets.xcassets/tabbar/tabbar_video.imageset/tabbar_video@2x.png deleted file mode 100644 index 51aead9..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_video.imageset/tabbar_video@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_video.imageset/tabbar_video@3x.png b/TTNews/Assets.xcassets/tabbar/tabbar_video.imageset/tabbar_video@3x.png deleted file mode 100644 index d733e9a..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_video.imageset/tabbar_video@3x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_video_hl.imageset/Contents.json b/TTNews/Assets.xcassets/tabbar/tabbar_video_hl.imageset/Contents.json deleted file mode 100644 index 487d920..0000000 --- a/TTNews/Assets.xcassets/tabbar/tabbar_video_hl.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_video_hl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tabbar_video_hl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "original" - } -} \ No newline at end of file diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_video_hl.imageset/tabbar_video_hl@2x.png b/TTNews/Assets.xcassets/tabbar/tabbar_video_hl.imageset/tabbar_video_hl@2x.png deleted file mode 100644 index c20c2e8..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_video_hl.imageset/tabbar_video_hl@2x.png and /dev/null differ diff --git a/TTNews/Assets.xcassets/tabbar/tabbar_video_hl.imageset/tabbar_video_hl@3x.png b/TTNews/Assets.xcassets/tabbar/tabbar_video_hl.imageset/tabbar_video_hl@3x.png deleted file mode 100644 index b4abf7a..0000000 Binary files a/TTNews/Assets.xcassets/tabbar/tabbar_video_hl.imageset/tabbar_video_hl@3x.png and /dev/null differ diff --git a/TTNews/Classes/Me/Controller/AppInfoViewController.h b/TTNews/Classes/Me/Controller/AppInfoViewController.h deleted file mode 100644 index 00fe2ef..0000000 --- a/TTNews/Classes/Me/Controller/AppInfoViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// AppInfoViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/18. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface AppInfoViewController : UIViewController - -@end diff --git a/TTNews/Classes/Me/Controller/AppInfoViewController.m b/TTNews/Classes/Me/Controller/AppInfoViewController.m deleted file mode 100644 index 4f9a41d..0000000 --- a/TTNews/Classes/Me/Controller/AppInfoViewController.m +++ /dev/null @@ -1,45 +0,0 @@ -// -// AppInfoViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/18. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "AppInfoViewController.h" -#import "TTConst.h" - -@interface AppInfoViewController () -@property (weak, nonatomic) IBOutlet UILabel *appNameLabel; - -@end - -@implementation AppInfoViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - self.title = @"关于"; - // Do any additional setup after loading the view from its nib. -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; -} - - -/* -#pragma mark - Navigation - -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. -} -*/ - -@end diff --git a/TTNews/Classes/Me/Controller/AppInfoViewController.xib b/TTNews/Classes/Me/Controller/AppInfoViewController.xib deleted file mode 100644 index 8404734..0000000 --- a/TTNews/Classes/Me/Controller/AppInfoViewController.xib +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/Me/Controller/EditUserInfoViewController.h b/TTNews/Classes/Me/Controller/EditUserInfoViewController.h deleted file mode 100644 index 9398e0f..0000000 --- a/TTNews/Classes/Me/Controller/EditUserInfoViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// EditUserInfoViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/18. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface EditUserInfoViewController : UIViewController - -@end diff --git a/TTNews/Classes/Me/Controller/EditUserInfoViewController.m b/TTNews/Classes/Me/Controller/EditUserInfoViewController.m deleted file mode 100644 index a01576e..0000000 --- a/TTNews/Classes/Me/Controller/EditUserInfoViewController.m +++ /dev/null @@ -1,78 +0,0 @@ -// -// EditUserInfoViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/18. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "EditUserInfoViewController.h" -#import "UIImage+Extension.h" -#import "TTConst.h" -#import - -@interface EditUserInfoViewController () -@property (weak, nonatomic) IBOutlet UIImageView *headerImageView; -@property (weak, nonatomic) IBOutlet UITextField *nameTextField; -@property (weak, nonatomic) IBOutlet UITextView *signatureTextView; - -@end - -@implementation EditUserInfoViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - self.view.dk_backgroundColorPicker = DKColorPickerWithKey(BG); - - [self setupBasic]; -} - --(void)setupBasic { - self.title = @"个人信息"; - NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"headerImage"]; - self.headerImageView.image = [UIImage imageWithContentsOfFile:path]; - self.headerImageView.userInteractionEnabled = YES; - [self.headerImageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(changeHeaderImage)]]; - self.headerImageView.layer.cornerRadius = self.headerImageView.frame.size.width *0.5; - self.headerImageView.layer.masksToBounds = YES; - self.nameTextField.text = [[NSUserDefaults standardUserDefaults] stringForKey:UserNameKey]; - self.signatureTextView.text = [[NSUserDefaults standardUserDefaults] stringForKey:UserSignatureKey]; - self.signatureTextView.layer.cornerRadius = 5; - self.signatureTextView.layer.masksToBounds = YES; -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [[NSUserDefaults standardUserDefaults] setObject:self.nameTextField.text forKey:UserNameKey]; - [[NSUserDefaults standardUserDefaults] setObject:self.signatureTextView.text forKey:UserSignatureKey]; -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - -} - - -- (void)changeHeaderImage { - UIImagePickerController *controller = [[UIImagePickerController alloc] init]; - controller.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; - controller.allowsEditing = YES; - controller.delegate = self; - [self presentViewController:controller animated:YES completion:nil]; -} - -- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(nullable NSDictionary *)editingInfo { - self.headerImageView.image = [image circleImage]; - NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"headerImage"]; - [UIImagePNGRepresentation(self.headerImageView.image) writeToFile:path atomically:YES]; - [self dismissViewControllerAnimated:YES completion:nil]; - -} - --(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - [self.view endEditing:YES]; -} - - - -@end diff --git a/TTNews/Classes/Me/Controller/EditUserInfoViewController.xib b/TTNews/Classes/Me/Controller/EditUserInfoViewController.xib deleted file mode 100644 index 040055a..0000000 --- a/TTNews/Classes/Me/Controller/EditUserInfoViewController.xib +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. - - - - - - - - diff --git a/TTNews/Classes/Me/Controller/MeTableViewController.h b/TTNews/Classes/Me/Controller/MeTableViewController.h deleted file mode 100644 index 8b26424..0000000 --- a/TTNews/Classes/Me/Controller/MeTableViewController.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// MeTableViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/25. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@protocol MeTableViewControllerDelegate -@optional -- (void)shakeCanChangeSkin:(BOOL)status; - -@end - -@interface MeTableViewController : UITableViewController - -@property(nonatomic, weak) id delegate; -@property (nonatomic, weak) UISwitch *changeSkinSwitch; - -@end diff --git a/TTNews/Classes/Me/Controller/MeTableViewController.m b/TTNews/Classes/Me/Controller/MeTableViewController.m deleted file mode 100644 index bf6bfd7..0000000 --- a/TTNews/Classes/Me/Controller/MeTableViewController.m +++ /dev/null @@ -1,222 +0,0 @@ -// -// MeTableViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/25. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "MeTableViewController.h" -#import -#import "TTDataTool.h" -#import -#import "UIImage+Extension.h" -#import "TTConst.h" -#import "SendFeedbackViewController.h" -#import "AppInfoViewController.h" -#import "EditUserInfoViewController.h" -#import "UIImage+Extension.h" -#import "UserInfoCell.h" -#import "SwitchCell.h" -#import -#import "TwoLabelCell.h" -#import "DisclosureCell.h" - -static NSString *const UserInfoCellIdentifier = @"UserInfoCell"; -static NSString *const SwitchCellIdentifier = @"SwitchCell"; -static NSString *const TwoLabelCellIdentifier = @"TwoLabelCell"; -static NSString *const DisclosureCellIdentifier = @"DisclosureCell"; - -@interface MeTableViewController () - -@property (nonatomic, copy) NSString *userName; -@property (nonatomic, weak) UISwitch *shakeCanChangeSkinSwitch; -@property (nonatomic, weak) UISwitch *imageDownLoadModeSwitch; -@property (nonatomic, assign) CGFloat cacheSize; -@property (nonatomic, copy) NSString *currentSkinModel; - - -@end - -CGFloat const footViewHeight = 30; - -@implementation MeTableViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - [self caculateCacheSize]; - [self setupBasic]; - -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [SVProgressHUD dismiss]; -} - --(void)setupBasic{ - self.automaticallyAdjustsScrollViewInsets = NO; - self.tableView.contentInset = UIEdgeInsetsMake(CGRectGetMaxY(self.navigationController.navigationBar.frame) + 30, 0, 0, 0); - self.tableView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x000000, 0xfafafa); - self.tableView.dk_separatorColorPicker = DKColorPickerWithKey(SEP); - self.navigationController.navigationBar.dk_barTintColorPicker = DKColorPickerWithRGB(0xfa5054,0x444444,0xfa5054); - [self.tableView registerClass:[UserInfoCell class] forCellReuseIdentifier:UserInfoCellIdentifier]; - [self.tableView registerClass:[SwitchCell class] forCellReuseIdentifier:SwitchCellIdentifier]; - [self.tableView registerClass:[TwoLabelCell class] forCellReuseIdentifier:TwoLabelCellIdentifier]; - [self.tableView registerClass:[DisclosureCell class] forCellReuseIdentifier:DisclosureCellIdentifier]; - - - -} - --(void)caculateCacheSize { - float imageCache = [[SDImageCache sharedImageCache] getSize]/1024.0/1024.0; -// NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"data.sqlite"]; -// NSFileManager *fileManager = [NSFileManager defaultManager]; -// float sqliteCache = [fileManager attributesOfItemAtPath:path error:nil].fileSize/1024.0/1024.0; - self.cacheSize = imageCache; -} - -#pragma mark - Table view data source - -#pragma mark -UITableViewDataSource 返回tableView有多少组 -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 2; -} - -#pragma mark -UITableViewDataSource 返回tableView每一组有多少行 -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - if (section == 0) return 1; - return 5; -} - --(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { - return footViewHeight; -} - -#pragma mark -UITableViewDataSource 返回indexPath对应的cell的高度 --(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - if (indexPath.section == 0) return 100; - - return 44; -} - --(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { - UIView *footView = [[UIView alloc] init]; - footView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, footViewHeight); - UIView *lineView1 = [[UIView alloc] init]; - lineView1.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 1); - [footView addSubview:lineView1]; - UIView *lineView2 = [[UIView alloc] init]; - lineView2.frame = CGRectMake(0, footViewHeight - 1, [UIScreen mainScreen].bounds.size.width, 1); - [footView addSubview:lineView2]; - - if (section==2) { - [lineView2 removeFromSuperview]; - } - return footView; -} - - --(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - if(indexPath.section == 0) { - UserInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:UserInfoCellIdentifier]; - cell.textLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - - NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"headerImage"]; - UIImage *image = [UIImage imageWithContentsOfFile:path]; - if (image == nil) { - image = [UIImage imageNamed:@"defaultUserIcon"]; - [UIImagePNGRepresentation(image) writeToFile:path atomically:YES]; - } - NSString *name = [[NSUserDefaults standardUserDefaults] stringForKey:UserNameKey]; - NSString *content = [[NSUserDefaults standardUserDefaults] stringForKey:UserSignatureKey]; - [cell setAvatarImage:image Name:name Signature:content]; - - return cell; - } - - if (indexPath.section == 1&&indexPath.row <2) { - SwitchCell *cell = [tableView dequeueReusableCellWithIdentifier:SwitchCellIdentifier]; - cell.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - if (indexPath.row == 0) { - cell.leftLabel.text = @"摇一摇夜间模式"; - self.shakeCanChangeSkinSwitch = cell.theSwitch; - BOOL status = [[NSUserDefaults standardUserDefaults] boolForKey:IsShakeCanChangeSkinKey]; - cell.theSwitch.on = status; - [cell.theSwitch addTarget:self action:@selector(switchDidChange:) forControlEvents:UIControlEventValueChanged]; - } else if (indexPath.row == 1) { - cell.leftLabel.text = @"夜间模式"; - cell.theSwitch.on= [self.dk_manager.themeVersion isEqualToString:DKThemeVersionNight]?YES:NO; - self.changeSkinSwitch = cell.theSwitch; - [cell.theSwitch addTarget:self action:@selector(switchDidChange:) forControlEvents:UIControlEventValueChanged]; - } - return cell; - } - - //第三组cell - if (indexPath.section == 1 && indexPath.row == 2) { - TwoLabelCell *cell = [tableView dequeueReusableCellWithIdentifier:TwoLabelCellIdentifier]; - cell.leftLabel.text = @"清除缓存"; - cell.rightLabel.text = [NSString stringWithFormat:@"%.1f MB",self.cacheSize]; - return cell; - } - - DisclosureCell *cell = [tableView dequeueReusableCellWithIdentifier:DisclosureCellIdentifier]; - if (indexPath.row == 3) { - cell.leftLabel.text = @"反馈"; - } else if(indexPath.row == 4) { - cell.leftLabel.text = @"关于"; - } - return cell; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - if (indexPath.section == 0 && indexPath.row == 0) { - [self.navigationController pushViewController:[[EditUserInfoViewController alloc] init] animated:YES]; - } else if (indexPath.section == 1 && indexPath.row ==2) { - [SVProgressHUD show]; - [TTDataTool deletePartOfCacheInSqlite]; - [[SDImageCache sharedImageCache] clearDisk]; - [SVProgressHUD showSuccessWithStatus:@"缓存清除完毕!"]; - TwoLabelCell *cell = [tableView cellForRowAtIndexPath:indexPath]; - cell.rightLabel.text = [NSString stringWithFormat:@"0.0MB"]; - } else if (indexPath.section == 1 && indexPath.row == 3) { - [self.navigationController pushViewController:[[SendFeedbackViewController alloc] init] animated:YES]; - } else if (indexPath.section == 1 && indexPath.row == 4) { - [self.navigationController pushViewController:[[AppInfoViewController alloc] init] animated:YES]; - } -} - --(void)switchDidChange:(UISwitch *)theSwitch { - if (theSwitch == self.changeSkinSwitch) { - if (theSwitch.on == YES) {//切换至夜间模式 - self.dk_manager.themeVersion = DKThemeVersionNight; - self.tabBarController.tabBar.barTintColor = [UIColor colorWithRed:34/255.0 green:34/255.0 blue:34/255.0 alpha:1.0]; - - } else { - self.dk_manager.themeVersion = DKThemeVersionNormal; - self.tabBarController.tabBar.barTintColor = [UIColor whiteColor]; - - } - - } else if (theSwitch == self.shakeCanChangeSkinSwitch) {//摇一摇夜间模式 - BOOL status = self.shakeCanChangeSkinSwitch.on; - [[NSUserDefaults standardUserDefaults] setObject:@(status) forKey:IsShakeCanChangeSkinKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; - if([self.delegate respondsToSelector:@selector(shakeCanChangeSkin:)]) { - [self.delegate shakeCanChangeSkin:status]; - } - } -} - --(void)didReceiveMemoryWarning { - [[SDImageCache sharedImageCache] clearDisk]; - -} -@end diff --git a/TTNews/Classes/Me/Controller/SendFeedbackViewController.h b/TTNews/Classes/Me/Controller/SendFeedbackViewController.h deleted file mode 100644 index 199a01f..0000000 --- a/TTNews/Classes/Me/Controller/SendFeedbackViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// SendFeedbackViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/18. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface SendFeedbackViewController : UIViewController - -@end diff --git a/TTNews/Classes/Me/Controller/SendFeedbackViewController.m b/TTNews/Classes/Me/Controller/SendFeedbackViewController.m deleted file mode 100644 index 2ca02e0..0000000 --- a/TTNews/Classes/Me/Controller/SendFeedbackViewController.m +++ /dev/null @@ -1,108 +0,0 @@ -// -// SendFeedbackViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/18. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "SendFeedbackViewController.h" -#import "TTConst.h" -#import -#import - -@interface SendFeedbackViewController () - -@property (nonatomic, weak) UITextView *textView; -@property (nonatomic, weak) UILabel *placeholderLabel; - -@end - -@implementation SendFeedbackViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - [self setupBasic]; -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; - -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - - --(void)keyboardWillChangeFrame:(NSNotification *)notification { - CGRect frame = [notification.userInfo[@"UIKeyboardFrameEndUserInfoKey"] CGRectValue]; - CGFloat margin = 20; - self.textView.frame = CGRectMake(margin, CGRectGetMaxY(self.navigationController.navigationBar.frame) + margin, [UIScreen mainScreen].bounds.size.width - 2*margin, frame.origin.y - 2*margin - CGRectGetMaxY(self.navigationController.navigationBar.frame)); -} - --(void)setupBasic { - self.view.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x343434, 0xfafafa); - self.title = @"反馈"; - self.automaticallyAdjustsScrollViewInsets = NO; - UIBarButtonItem *redItem = [[UIBarButtonItem alloc] initWithTitle:@"发送" style:UIBarButtonItemStyleDone target:self action:@selector(sendFeedBack)]; - redItem.dk_tintColorPicker = DKColorPickerWithKey(TINT); - self.navigationItem.rightBarButtonItem =redItem; - self.navigationItem.rightBarButtonItem.tintColor = [UIColor whiteColor]; - - UITextView *textView = [[UITextView alloc] init]; - self.textView = textView; - textView.dk_textColorPicker = DKColorPickerWithKey(TEXT); - CGFloat margin = 20; - textView.frame = CGRectMake(margin, CGRectGetMaxY(self.navigationController.navigationBar.frame) + margin, [UIScreen mainScreen].bounds.size.width - 2*margin, [UIScreen mainScreen].bounds.size.height - 2*margin - CGRectGetMaxY(self.navigationController.navigationBar.frame)); - textView.delegate = self; - textView.font = [UIFont systemFontOfSize:18]; - [textView becomeFirstResponder]; - textView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag; - textView.layer.cornerRadius = 5; - textView.layer.masksToBounds = YES; - [self.view addSubview:textView]; - - - UILabel *placeholderLabel = [[UILabel alloc] init]; - self.placeholderLabel = placeholderLabel; - placeholderLabel.text = [NSString stringWithFormat:@"请输入反馈,我们将为您不断改进"]; - placeholderLabel.textColor = [UIColor grayColor]; - placeholderLabel.frame = CGRectMake(textView.frame.origin.x + 10, textView.frame.origin.y + 10, textView.frame.size.width - 20, 20); - [self.view addSubview:placeholderLabel]; - - CGFloat buttonWidth = 30; - UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; - button.frame = CGRectMake([UIScreen mainScreen].bounds.size.width - buttonWidth - 10, 5, buttonWidth, buttonWidth); - [button addTarget:self action:@selector(dissmissKeyboard) forControlEvents:UIControlEventTouchUpInside]; - [button setBackgroundImage:[UIImage imageNamed:@"KBSkinToolbar_icon_hide"] forState:UIControlStateNormal]; - UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 40)]; - view.backgroundColor = [UIColor whiteColor]; - [view addSubview:button]; - textView.inputAccessoryView = view; -} - --(void)sendFeedBack { - [SVProgressHUD showSuccessWithStatus:@"发送成功!"]; - [self.navigationController popViewControllerAnimated:YES]; -} - --(void)dissmissKeyboard { - [self.textView resignFirstResponder]; -} - -- (void)textViewDidChange:(UITextView *)textView { - if ([textView.text isEqualToString:@""]) { - self.placeholderLabel.hidden = NO; - } else { - self.placeholderLabel.hidden = YES; - } -} - --(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - [self.textView resignFirstResponder]; -} - -@end diff --git a/TTNews/Classes/Me/View/DisclosureCell.h b/TTNews/Classes/Me/View/DisclosureCell.h deleted file mode 100644 index ae808e7..0000000 --- a/TTNews/Classes/Me/View/DisclosureCell.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// DisclosureCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/8/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface DisclosureCell : UITableViewCell -@property (nonatomic, weak) UILabel *leftLabel; - -@end diff --git a/TTNews/Classes/Me/View/DisclosureCell.m b/TTNews/Classes/Me/View/DisclosureCell.m deleted file mode 100644 index 4637d84..0000000 --- a/TTNews/Classes/Me/View/DisclosureCell.m +++ /dev/null @@ -1,27 +0,0 @@ -// -// DisclosureCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/8/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "DisclosureCell.h" -#import - -@implementation DisclosureCell --(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { - self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier]; - if (self) { - UILabel *label = [[UILabel alloc] init]; - self.leftLabel = label; - label.frame = CGRectMake(15, 0, 120, self.frame.size.height); - label.dk_textColorPicker = DKColorPickerWithKey(TEXT); - [self addSubview:label]; - self.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - self.selectionStyle = UITableViewCellSelectionStyleNone; - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - } - return self; -} -@end diff --git a/TTNews/Classes/Me/View/SwitchCell.h b/TTNews/Classes/Me/View/SwitchCell.h deleted file mode 100644 index 273a688..0000000 --- a/TTNews/Classes/Me/View/SwitchCell.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// SwitchCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/8/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - - -@interface SwitchCell : UITableViewCell -@property (nonatomic, weak) UILabel *leftLabel; -@property (nonatomic, weak) UISwitch *theSwitch; -@end diff --git a/TTNews/Classes/Me/View/SwitchCell.m b/TTNews/Classes/Me/View/SwitchCell.m deleted file mode 100644 index 303b106..0000000 --- a/TTNews/Classes/Me/View/SwitchCell.m +++ /dev/null @@ -1,35 +0,0 @@ - -// -// SwitchCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/8/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "SwitchCell.h" -#import - -@implementation SwitchCell - --(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { - self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier]; - if (self) { - UILabel *label = [[UILabel alloc] init]; - self.leftLabel = label; - label.frame = CGRectMake(15, 0, 120, self.frame.size.height); - label.dk_textColorPicker = DKColorPickerWithKey(TEXT); - [self addSubview:label]; - UISwitch *theSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(self.frame.size.width - 60, 6, 50, self.frame.size.height)]; - self.theSwitch = theSwitch; - [self addSubview:theSwitch]; - self.selectionStyle = UITableViewCellSelectionStyleNone; - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - - } - return self; -} - - - -@end diff --git a/TTNews/Classes/Me/View/TwoLabelCell.h b/TTNews/Classes/Me/View/TwoLabelCell.h deleted file mode 100644 index f7d7777..0000000 --- a/TTNews/Classes/Me/View/TwoLabelCell.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// TwoLabelCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/8/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TwoLabelCell : UITableViewCell -@property (nonatomic, weak) UILabel *leftLabel; -@property (nonatomic, weak) UILabel *rightLabel; - -@end diff --git a/TTNews/Classes/Me/View/TwoLabelCell.m b/TTNews/Classes/Me/View/TwoLabelCell.m deleted file mode 100644 index 7c1d445..0000000 --- a/TTNews/Classes/Me/View/TwoLabelCell.m +++ /dev/null @@ -1,34 +0,0 @@ - -// -// TwoLabelCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/8/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TwoLabelCell.h" -#import - -@implementation TwoLabelCell --(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { - self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier]; - if (self) { - UILabel *label1 = [[UILabel alloc] init]; - self.leftLabel = label1; - label1.frame = CGRectMake(15, 0, 120, self.frame.size.height); - label1.dk_textColorPicker = DKColorPickerWithKey(TEXT); - [self addSubview:label1]; - - UILabel *label2 = [[UILabel alloc] init]; - self.rightLabel = label2; - label2.frame = CGRectMake(self.frame.size.width-90, 0, 80, self.frame.size.height); - label2.textAlignment= NSTextAlignmentRight; - label2.dk_textColorPicker = DKColorPickerWithKey(TEXT); - [self addSubview:label2]; - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - - } - return self; -} -@end diff --git a/TTNews/Classes/Me/View/UserInfoCell.h b/TTNews/Classes/Me/View/UserInfoCell.h deleted file mode 100644 index 46079a2..0000000 --- a/TTNews/Classes/Me/View/UserInfoCell.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// UserInfoCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/8/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface UserInfoCell : UITableViewCell --(void)setAvatarImage:(UIImage *)image Name:(NSString *)name Signature:(NSString *)content; -@end diff --git a/TTNews/Classes/Me/View/UserInfoCell.m b/TTNews/Classes/Me/View/UserInfoCell.m deleted file mode 100644 index 35719ce..0000000 --- a/TTNews/Classes/Me/View/UserInfoCell.m +++ /dev/null @@ -1,69 +0,0 @@ - -// -// UserInfoCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/8/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "UserInfoCell.h" -#import - -@interface UserInfoCell() -@property (nonatomic, weak) UIImageView *avatarImageView; -@property (nonatomic, weak) UILabel *nameLabel; -@property (nonatomic, weak) UILabel *contentLabel; -@end -@implementation UserInfoCell - --(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { - self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; - if (self) { - CGFloat cellHeight = 100; - CGFloat margin = 10; - CGFloat kScreenWidth = [UIScreen mainScreen].bounds.size.width; - - UIImageView *avatarImageView = [[UIImageView alloc] init]; - self.avatarImageView = avatarImageView; - avatarImageView.frame =CGRectMake(margin, margin, cellHeight - 2*margin, cellHeight - 2*margin); - - avatarImageView.layer.cornerRadius = avatarImageView.frame.size.width * 0.5; - avatarImageView.layer.masksToBounds = YES; - [self addSubview:avatarImageView]; - - UILabel *nameLabel = [[UILabel alloc] init]; - self.nameLabel = nameLabel; - CGFloat nameLabelHeight = 21.5; - nameLabel.font = [UIFont systemFontOfSize:18]; - - nameLabel.frame = CGRectMake(CGRectGetMaxX(avatarImageView.frame) + margin, avatarImageView.frame.origin.y +avatarImageView.frame.size.height*0.5 - nameLabelHeight-0.5*margin, kScreenWidth - 3*margin -avatarImageView.frame.size.width, nameLabelHeight); - nameLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - [self addSubview:nameLabel]; - - UILabel *contentLabel = [[UILabel alloc] init]; - self.contentLabel = contentLabel; - CGFloat contentLabelHeight = 17.5; - - contentLabel.font = [UIFont systemFontOfSize:14]; - contentLabel.textColor = [UIColor grayColor]; - contentLabel.frame = CGRectMake(CGRectGetMaxX(avatarImageView.frame) + margin, avatarImageView.frame.origin.y +avatarImageView.frame.size.height*0.5+0.5*margin, kScreenWidth - 3*margin -avatarImageView.frame.size.width, contentLabelHeight); - contentLabel.lineBreakMode = NSLineBreakByTruncatingTail; - contentLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - [self addSubview:contentLabel]; - - self.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - self.selectionStyle = UITableViewCellSelectionStyleNone; - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - - } - return self; -} - -- (void)setAvatarImage:(UIImage *)image Name:(NSString *)name Signature:(NSString *)content { - self.avatarImageView.image = image; - self.nameLabel.text = name; - self.contentLabel.text = content; - -} -@end diff --git a/TTNews/Classes/News/Controller/ContentTableViewController.h b/TTNews/Classes/News/Controller/ContentTableViewController.h deleted file mode 100644 index 7a03779..0000000 --- a/TTNews/Classes/News/Controller/ContentTableViewController.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// ContentTableViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/26. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -@class TTNormalNews; - -@interface ContentTableViewController : UITableViewController - -@property(nonatomic, strong) TTNormalNews *news; -@property (nonatomic, copy) NSString *channelId; -@property (nonatomic, copy) NSString *channelName; -@property(nonatomic,copy) NSString *urlString; -@property (nonatomic,assign) NSInteger index; - -@end diff --git a/TTNews/Classes/News/Controller/ContentTableViewController.m b/TTNews/Classes/News/Controller/ContentTableViewController.m deleted file mode 100644 index 2af91f5..0000000 --- a/TTNews/Classes/News/Controller/ContentTableViewController.m +++ /dev/null @@ -1,292 +0,0 @@ -// -// ContentTableViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/26. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "ContentTableViewController.h" -#import -#import -#import "SinglePictureNewsTableViewCell.h" -#import "MultiPictureTableViewCell.h" -#import "DetailViewController.h" -#import "ShowMultiPictureViewController.h" -#import "TTNormalNewsFetchDataParameter.h" -#import "TTDataTool.h" -#import "TTConst.h" -#import -#import -#import "SXNetworkTools.h" -#import "SXNewsEntity.h" -#import -#import "TopTextTableViewCell.h" -#import "TopPictureTableViewCell.h" -#import "BigPictureTableViewCell.h" -#import - -@interface ContentTableViewController () - -@property (nonatomic, strong) NSMutableArray *headerNewsArray; -@property (nonatomic, assign) NSInteger currentPage; -@property (nonatomic, strong) NSMutableArray *normalNewsArray; -@property (nonatomic, copy) NSString *currentSkinModel; -@property (nonatomic, strong) NSMutableArray *arrayList; -@property(nonatomic,assign)BOOL update; - -@end - -static NSString * const singlePictureCell = @"SinglePictureCell"; -static NSString * const multiPictureCell = @"MultiPictureCell"; -static NSString * const bigPictureCell = @"BigPictureCell"; -static NSString * const topTextPictureCell = @"TopTextPictureCell"; -static NSString * const topPictureCell = @"TopPictureCell"; - -@implementation ContentTableViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - - [self setupBasic]; - [self setupRefresh]; - -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - if (self.update == YES) { - [self.tableView.mj_header beginRefreshing]; - self.update = NO; - } -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [SVProgressHUD dismiss]; -} - -#pragma mark --private Method--设置tableView --(void)setupBasic { - self.tableView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x000000, 0xfafafa); - - self.automaticallyAdjustsScrollViewInsets = NO; - self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; - self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(104, 0, 0, 0); - [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([TopPictureTableViewCell class]) bundle:nil] forCellReuseIdentifier:topPictureCell]; - [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([TopTextTableViewCell class]) bundle:nil] forCellReuseIdentifier:topTextPictureCell]; - [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([BigPictureTableViewCell class]) bundle:nil] forCellReuseIdentifier:bigPictureCell]; - [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([SinglePictureNewsTableViewCell class]) bundle:nil] forCellReuseIdentifier:singlePictureCell]; - [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([MultiPictureTableViewCell class]) bundle:nil] forCellReuseIdentifier:multiPictureCell]; -} - - -#pragma mark --private Method--初始化刷新控件 --(void)setupRefresh { - self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadData)]; - self.tableView.mj_header.automaticallyChangeAlpha = YES; - [self.tableView.mj_header beginRefreshing]; - self.tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; - self.currentPage = 1; -} - - -#pragma mark - /************************* 刷新数据 ***************************/ -// ------下拉刷新 -- (void)loadData -{ - // http://c.m.163.com//nc/article/headline/T1348647853363/0-30.html - NSString *allUrlstring = [NSString stringWithFormat:@"/nc/article/%@/0-20.html",self.urlString]; - [self loadDataForType:1 withURL:allUrlstring]; -} - -- (void)loadMoreData -{ - NSString *allUrlstring = [NSString stringWithFormat:@"/nc/article/%@/%ld-20.html",self.urlString,(self.arrayList.count - self.arrayList.count%10)]; - // NSString *allUrlstring = [NSString stringWithFormat:@"/nc/article/%@/%ld-20.html",self.urlString,self.arrayList.count]; - [self loadDataForType:2 withURL:allUrlstring]; -} - -- (void)loadDataForType:(int)type withURL:(NSString *)allUrlstring -{ - [[[SXNetworkTools sharedNetworkTools] GET:allUrlstring parameters:nil progress:nil success:^(NSURLSessionDataTask *task, NSDictionary* responseObject) { - NSLog(@"%@",allUrlstring); - NSString *key = [responseObject.keyEnumerator nextObject]; - - NSArray *temArray = responseObject[key]; - - NSArray *arrayM = [SXNewsEntity mj_objectArrayWithKeyValuesArray:temArray]; - - if (type == 1) { - self.arrayList = [arrayM mutableCopy]; - [self.tableView.mj_header endRefreshing]; - [self.tableView reloadData]; - }else if(type == 2){ - [self.arrayList addObjectsFromArray:arrayM]; - - [self.tableView.mj_footer endRefreshing]; - [self.tableView reloadData]; - } - - } failure:^(NSURLSessionDataTask *task, NSError *error) { - NSLog(@"%@",error); - }] resume]; -}// ------想把这里改成block来着 - - - -#pragma mark -UITableViewDataSource 返回tableView有多少组 -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 1; -} - -#pragma mark -UITableViewDataSource 返回tableView每一组有多少行 -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return self.arrayList.count; -} - -#pragma mark -UITableViewDataSource 返回indexPath对应的cell -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - - SXNewsEntity *NewsModel = self.arrayList[indexPath.row]; - if (NewsModel.hasHead && NewsModel.photosetID) { - TopPictureTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:topPictureCell]; - [cell.imgIcon sd_setImageWithURL:[NSURL URLWithString:NewsModel.imgsrc] placeholderImage:[UIImage imageNamed:@"302"]]; - cell.LblTitleLabel.text = NewsModel.title; - return cell; - }else if (NewsModel.hasHead){ - TopTextTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:topTextPictureCell]; - [cell.imgIcon sd_setImageWithURL:[NSURL URLWithString:NewsModel.imgsrc] placeholderImage:[UIImage imageNamed:@"302"]]; - cell.LblTitleLabel.text = NewsModel.title; - return cell; - }else if (NewsModel.imgType){ - BigPictureTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:bigPictureCell]; - [cell.imgIcon sd_setImageWithURL:[NSURL URLWithString:NewsModel.imgsrc] placeholderImage:[UIImage imageNamed:@"302"]]; - cell.LblTitleLabel.text = NewsModel.title; - cell.subTitleLabel.text = NewsModel.subtitle; - return cell; - }else if (NewsModel.imgextra){ - MultiPictureTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:multiPictureCell]; - cell.theTitle = NewsModel.title; - cell.imageUrls = [NSArray arrayWithObjects:NewsModel.imgsrc, NewsModel.imgextra[0], NewsModel.imgextra[0], nil]; - return cell; - }else{ - SinglePictureNewsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:singlePictureCell]; - cell.imageUrl = NewsModel.imgsrc; - cell.contentTittle = NewsModel.title; - cell.desc = NewsModel.digest; - - return cell; - } -// if (newsModel.imgextra){ -// MultiPictureTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:multiPictureCell]; -// cell.theTitle = newsModel.title; -// cell.imageUrls = [NSArray arrayWithObjects:newsModel.imgsrc, newsModel.imgextra[0], newsModel.imgextra[0], nil]; -// return cell; -// } else { -// SinglePictureNewsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:singlePictureCell]; -// cell.imageUrl = newsModel.imgsrc; -// cell.contentTittle = newsModel.title; -// cell.desc = newsModel.digest; -// -// return cell; -// } - -// TTNormalNews *news = self.normalNewsArray[indexPath.row]; -// if (news.normalNewsType == NormalNewsTypeMultiPicture) { -// MultiPictureTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:multiPictureCell]; -// cell.title = news.title; -// cell.imageUrls = news.imageurls; -// -// return cell; -// } else if (news.normalNewsType == NormalNewsTypeSigalPicture) { -// SinglePictureNewsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:singlePictureCell]; -// cell.contentTittle = news.title; -// cell.desc = news.desc; -// NSDictionary *dict = news.imageurls.firstObject; -// if (dict) { -// cell.imageUrl = dict[@"url"]; -// } -// return cell; -// } else { -// NoPictureNewsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:noPictureCell]; -// cell.titleText = news.title; -// cell.contentText = news.desc; -// -// return cell; -// } -} - -#pragma mark -UITableViewDataSource 返回indexPath对应的cell的高度 --(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - SXNewsEntity *NewsModel = self.arrayList[indexPath.row]; -// -// return newsModel.cellHeight; - if (NewsModel.hasHead && NewsModel.photosetID){ - return 245; - }else if(NewsModel.hasHead) { - return 245; - }else if(NewsModel.imgType) { - return 170; - }else if (NewsModel.imgextra){ - return 130; - }else{ - return 100; - } -} - -#pragma mark -UITableViewDelegate 点击了某个cell --(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - - - SXNewsEntity *NewsModel = self.arrayList[indexPath.row]; - if (NewsModel.hasHead && NewsModel.photosetID) { - [self pushToDetailViewControllerWithUrl:NewsModel.url]; - }else if (NewsModel.hasHead){ - [self pushToDetailViewControllerWithUrl:NewsModel.url]; - - }else if (NewsModel.imgType){ - [self pushToDetailViewControllerWithUrl:NewsModel.url]; - - }else if (NewsModel.imgextra){ - ShowMultiPictureViewController *viewController = [[ShowMultiPictureViewController alloc] init]; - viewController.imageUrls = [NSArray arrayWithObjects:NewsModel.imgsrc, NewsModel.imgextra[0], NewsModel.imgextra[0], nil]; - NSString *text = NewsModel.digest; - if (text == nil || [text isEqualToString:@""]) { - text = NewsModel.title; - } - viewController.text = text; - [self.navigationController pushViewController:viewController animated:YES]; - }else{ - [self pushToDetailViewControllerWithUrl:NewsModel.url]; - } - -} - -#pragma mark --private Method--点击了某一条新闻,调转到新闻对应的网页去 --(void)pushToDetailViewControllerWithUrl:(NSString *)url { - DetailViewController *viewController = [[DetailViewController alloc] init]; - viewController.url = url; - [self.navigationController pushViewController:viewController animated:YES]; -} - -#pragma mark --懒加载--normalNewsArray --(NSMutableArray *)normalNewsArray { - if (!_normalNewsArray) { - _normalNewsArray = [NSMutableArray array]; - } - return _normalNewsArray; -} - -#pragma mark --懒加载--headerNewsArray --(NSMutableArray *)headerNewsArray { - if (!_headerNewsArray) { - _headerNewsArray = [NSMutableArray array]; - } - return _headerNewsArray; -} --(void)didReceiveMemoryWarning { - [[SDImageCache sharedImageCache] clearDisk]; - -} -@end diff --git a/TTNews/Classes/News/Controller/DetailViewController.h b/TTNews/Classes/News/Controller/DetailViewController.h deleted file mode 100644 index c7f0f22..0000000 --- a/TTNews/Classes/News/Controller/DetailViewController.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// DetailViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/29. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface DetailViewController : UIViewController - -@property (nonatomic, copy) NSString *url; - -@end diff --git a/TTNews/Classes/News/Controller/DetailViewController.m b/TTNews/Classes/News/Controller/DetailViewController.m deleted file mode 100644 index e324dda..0000000 --- a/TTNews/Classes/News/Controller/DetailViewController.m +++ /dev/null @@ -1,173 +0,0 @@ -// -// DetailViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/29. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "DetailViewController.h" -#import -#import -#import "TTConst.h" -#import "TTJudgeNetworking.h" -#import - -@interface DetailViewController () - -@property (nonatomic, weak) UIView *shadeView;//(页面模式时,用来使页面变暗) - -@property (nonatomic, weak) UIButton *collectButton; -@property (nonatomic, weak) UIWebView *webView; - -@property (nonatomic, weak) UIBarButtonItem *backItem; -@property (nonatomic, weak) UIBarButtonItem *forwardItem; -@property (nonatomic, weak) UIBarButtonItem *refreshItem; - -@end - -@implementation DetailViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - if([TTJudgeNetworking judge] == NO) { - [SVProgressHUD showErrorWithStatus:@"无网络连接"]; - [self.navigationController popViewControllerAnimated:YES]; - } - self.view.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x343434, 0xfafafa); - - [self setupWebView]; - [self setupNaigationBar]; - [self setupToolBars]; -// [self setupShadeView]; -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - [SVProgressHUD show]; - self.navigationController.toolbarHidden = NO; - -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [SVProgressHUD dismiss]; - self.navigationController.toolbarHidden = YES; -} - -#pragma mark --private Method--初始化webView -- (void)setupWebView { - UIWebView *webView = [[UIWebView alloc] init]; - self.webView = webView; - webView.frame = self.view.frame; - webView.delegate = self; - [self.view addSubview:webView]; - [SVProgressHUD show]; - - [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.url]]]; - -} - -#pragma mark --private Method--初始化NavigationBar --(void)setupNaigationBar { - UIButton *collectButton = [UIButton buttonWithType:UIButtonTypeCustom]; - self.collectButton = collectButton; - collectButton.frame =CGRectMake(0, 0, 30, 30); - [collectButton setImage:[UIImage imageNamed:@"navigationBarItem_favorite_normal"] forState:UIControlStateNormal]; - [collectButton setImage:[UIImage imageNamed:@"navigationBarItem_favorite_pressed"] forState:UIControlStateHighlighted]; - [self.collectButton setImage:[[UIImage imageNamed:@"navigationBarItem_favorited_normal"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]forState:UIControlStateSelected]; - [collectButton addTarget:self action:@selector(collectThisNews) forControlEvents:UIControlEventTouchUpInside]; - self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:collectButton]; -} - -#pragma mark --private Method--初始化toolBar -- (void)setupToolBars{ - UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithImage:[[UIImage imageNamed:@"toolbar_back_icon"] imageWithRenderingMode:UIImageRenderingModeAutomatic] style:UIBarButtonItemStylePlain target:self action:@selector(goBack)]; - backItem.enabled = NO; - self.backItem = backItem; - - UIBarButtonItem *forwardItem = [[UIBarButtonItem alloc] initWithImage:[[UIImage imageNamed:@"toolbar_forward_icon"] imageWithRenderingMode:UIImageRenderingModeAutomatic] style:UIBarButtonItemStylePlain target:self action:@selector(goForward)]; - forwardItem.enabled = NO; - self.forwardItem = forwardItem; - - UIBarButtonItem *flexibleItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; - - UIBarButtonItem *refreshItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refresh)]; - self.refreshItem = refreshItem; - - self.toolbarItems = @[backItem,forwardItem,flexibleItem,refreshItem]; - backItem.dk_tintColorPicker = DKColorPickerWithKey(TINT); - forwardItem.dk_tintColorPicker = DKColorPickerWithKey(TINT); - refreshItem.dk_tintColorPicker = DKColorPickerWithKey(TINT); - self.navigationController.toolbar.dk_tintColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); -} - -#pragma mark --private Method--初始化shadeView(页面模式时,用来使页面变暗) -- (void)setupShadeView { - UIView *shadeView = [[UIView alloc] init]; - self.shadeView = shadeView; - shadeView.backgroundColor = [UIColor blackColor]; - shadeView.alpha = 0.3; - shadeView.userInteractionEnabled = NO; - shadeView.frame = self.webView.bounds; - [self.webView addSubview:shadeView]; -} - -#pragma mark -UIWebViewDelegate-将要加载Webview -- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { - return YES; -} - -#pragma mark -UIWebViewDelegate-已经开始加载Webview -- (void)webViewDidStartLoad:(UIWebView *)webView { - double delayInSeconds = 0.5; - dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); - dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ - //执行事件 - [SVProgressHUD dismiss]; - }); -} - -#pragma mark -UIWebViewDelegate-已经加载Webview完毕 -- (void)webViewDidFinishLoad:(UIWebView *)webView { - [SVProgressHUD dismiss]; - self.backItem.enabled = webView.canGoBack; - self.forwardItem.enabled = webView.canGoForward; -} - -#pragma mark -UIWebViewDelegate-加载Webview失败 -- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { - [SVProgressHUD dismiss]; -} - - -#pragma mark --private Method--返回上一个页面 --(void)goBack { - [self.webView goBack]; -} - -#pragma mark --private Method--前进到下一个页面 --(void)goForward { - [self.webView goForward]; -} - -#pragma mark --private Method--刷新当前页面 --(void)refresh { - [self.webView reload]; -} - -#pragma mark --private Method--收藏这条新闻 --(void)collectThisNews { - self.collectButton.selected = !self.collectButton.selected; - if (self.collectButton.selected) { - [SVProgressHUD showSuccessWithStatus:@"收藏成功"]; - [self.collectButton setImage:[UIImage imageNamed:@"navigationBarItem_favorited_normal"] forState:UIControlStateNormal]; - [self.collectButton setImage:[UIImage imageNamed:@"navigationBarItem_favorited_pressed"] forState:UIControlStateHighlighted]; - } else { - [SVProgressHUD showSuccessWithStatus:@"取消收藏"]; - [self.collectButton setImage:[UIImage imageNamed:@"navigationBarItem_favorite_normal"] forState:UIControlStateNormal]; - [self.collectButton setImage:[UIImage imageNamed:@"navigationBarItem_favorite_pressed"] forState:UIControlStateHighlighted]; - } -} - -@end diff --git a/TTNews/Classes/News/Controller/NewsViewController.h b/TTNews/Classes/News/Controller/NewsViewController.h deleted file mode 100644 index add3b23..0000000 --- a/TTNews/Classes/News/Controller/NewsViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// NewsViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface NewsViewController : UIViewController - -@end diff --git a/TTNews/Classes/News/Controller/NewsViewController.m b/TTNews/Classes/News/Controller/NewsViewController.m deleted file mode 100644 index ecadc38..0000000 --- a/TTNews/Classes/News/Controller/NewsViewController.m +++ /dev/null @@ -1,158 +0,0 @@ -// -// NewsViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "NewsViewController.h" -#import -#import -#import -#import "ContentTableViewController.h" -#import "ChannelCollectionViewCell.h" -#import "TTJudgeNetworking.h" -#import "TTConst.h" -#import "TTTopChannelContianerView.h" -#import "ChannelsSectionHeaderView.h" -#import "TTNormalNews.h" -#import - -@interface NewsViewController() -@property (nonatomic, strong) NSMutableArray *currentChannelsArray; -@property (nonatomic, weak) TTTopChannelContianerView *topContianerView; -@property (nonatomic, weak) UIScrollView *contentScrollView; -@property (nonatomic, strong) NSArray *arrayLists; - -@end - -static NSString * const collectionCellID = @"ChannelCollectionCell"; -static NSString * const collectionViewSectionHeaderID = @"ChannelCollectionHeader"; - -@implementation NewsViewController -- (NSArray *)arrayLists -{ - if (_arrayLists == nil) { - _arrayLists = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"NewsURLs.plist" ofType:nil]]; - } - return _arrayLists; -} --(void)viewDidLoad { - self.automaticallyAdjustsScrollViewInsets = NO; -// self.isCellShouldShake = NO; - self.view.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x000000, 0xfafafa); - self.navigationController.navigationBar.dk_barTintColorPicker = DKColorPickerWithRGB(0xfa5054,0x444444,0xfa5054); - - [self setupTopContianerView]; - [self setupChildController]; - [self setupContentScrollView]; -// [self setupCollectionView]; - -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; -} - -#pragma mark --private Method--初始化子控制器 --(void)setupChildController { - for (NSInteger i = 0; i=-2) { - continue; - } else { - [theTableView removeFromSuperview]; - } - } - - } - - } -} - - - - -#pragma mark --UIScrollViewDelegate-- 滑动的减速动画结束后会调用这个方法 --(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - if (scrollView == self.contentScrollView) { - [self scrollViewDidEndScrollingAnimation:scrollView]; - NSInteger index = scrollView.contentOffset.x/self.contentScrollView.frame.size.width; - [self.topContianerView selectChannelButtonWithIndex:index]; - } -} - -#pragma mark --UICollectionViewDataSource-- 返回每个UICollectionViewCell发Size -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath -{ - CGFloat kDeviceWidth = [UIScreen mainScreen].bounds.size.width; - CGFloat kMargin = 10; - return CGSizeMake((kDeviceWidth - 5*kMargin)/4, 40); -} - -#pragma mark --TTTopChannelContianerViewDelegate--选择了某个新闻频道,更新scrollView的contenOffset -- (void)chooseChannelWithIndex:(NSInteger)index { - [self.contentScrollView setContentOffset:CGPointMake(self.contentScrollView.frame.size.width * index, 0) animated:YES]; -} - -#pragma mark --private Method--存储更新后的currentChannelsArray到偏好设置中 --(NSMutableArray *)currentChannelsArray { - if (!_currentChannelsArray) { - if (!_currentChannelsArray) { - _currentChannelsArray = [NSMutableArray arrayWithObjects:@"头条",@"NBA",@"手机",@"移动互联",@"娱乐",@"时尚",@"电影",@"科技", nil]; - } - } - return _currentChannelsArray; -} - -@end - diff --git a/TTNews/Classes/News/Controller/SXNetworkTools.h b/TTNews/Classes/News/Controller/SXNetworkTools.h deleted file mode 100755 index fb59f92..0000000 --- a/TTNews/Classes/News/Controller/SXNetworkTools.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// SXNetworkTools.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved.// - -#import - -@interface SXNetworkTools : AFHTTPSessionManager - -+ (instancetype)sharedNetworkTools; -+ (instancetype)sharedNetworkToolsWithoutBaseUrl; - -@end diff --git a/TTNews/Classes/News/Controller/SXNetworkTools.m b/TTNews/Classes/News/Controller/SXNetworkTools.m deleted file mode 100755 index bf9c107..0000000 --- a/TTNews/Classes/News/Controller/SXNetworkTools.m +++ /dev/null @@ -1,49 +0,0 @@ -// -// SXNetworkTools.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved.// - -#import "SXNetworkTools.h" - -@implementation SXNetworkTools - -+ (instancetype)sharedNetworkTools -{ - static SXNetworkTools*instance; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - - // http://c.m.163.com//nc/article/list/T1348649654285/0-20.html - // http://c.m.163.com/photo/api/set/0096/57255.json - // http://c.m.163.com/photo/api/set/54GI0096/57203.html - NSURL *url = [NSURL URLWithString:@"http://c.m.163.com/"]; - - NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - - instance = [[self alloc]initWithBaseURL:url sessionConfiguration:config]; - - instance.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", nil]; - }); - return instance; -} - -+ (instancetype)sharedNetworkToolsWithoutBaseUrl -{ - static SXNetworkTools*instance; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - - NSURL *url = [NSURL URLWithString:@""]; - - NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - - instance = [[self alloc]initWithBaseURL:url sessionConfiguration:config]; - - instance.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", nil]; - }); - return instance; -} - -@end diff --git a/TTNews/Classes/News/Controller/SXNewsEntity.h b/TTNews/Classes/News/Controller/SXNewsEntity.h deleted file mode 100755 index 7e30c15..0000000 --- a/TTNews/Classes/News/Controller/SXNewsEntity.h +++ /dev/null @@ -1,100 +0,0 @@ -// -// SXNewsEntity.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved.// - -#import -#import - -@interface SXNewsEntity : NSObject - -@property (nonatomic,copy) NSString *tname; -/** - * 新闻发布时间 - */ -@property (nonatomic,copy) NSString *ptime; -/** - * 标题 - */ -@property (nonatomic,copy) NSString *title; -/** - * 多图数组 - */ -@property (nonatomic,strong)NSArray *imgextra; -@property (nonatomic,copy) NSString *photosetID; -@property (nonatomic,copy)NSNumber *hasHead; -@property (nonatomic,copy)NSNumber *hasImg; -@property (nonatomic,copy) NSString *lmodify; -@property (nonatomic,copy) NSString *template; -@property (nonatomic,copy) NSString *skipType; -/** - * 跟帖人数 - */ -@property (nonatomic,copy)NSNumber *replyCount; -@property (nonatomic,copy)NSNumber *votecount; -@property (nonatomic,copy)NSNumber *voteCount; - -@property (nonatomic,copy) NSString *alias; -/** - * 新闻ID - */ -@property (nonatomic,copy) NSString *docid; -@property (nonatomic,assign)BOOL hasCover; -@property (nonatomic,copy)NSNumber *hasAD; -@property (nonatomic,copy)NSNumber *priority; -@property (nonatomic,copy) NSString *cid; -@property (nonatomic,strong)NSArray *videoID; -/** - * 图片连接 - */ -@property (nonatomic,copy) NSString *imgsrc; -@property (nonatomic,assign)BOOL hasIcon; -@property (nonatomic,copy) NSString *ename; -@property (nonatomic,copy) NSString *skipID; -@property (nonatomic,copy)NSNumber *order; -/** - * 描述 - */ -@property (nonatomic,copy) NSString *digest; - -@property (nonatomic,strong)NSArray *editor; - - -@property (nonatomic,copy) NSString *url_3w; -@property (nonatomic,copy) NSString *specialID; -@property (nonatomic,copy) NSString *timeConsuming; -@property (nonatomic,copy) NSString *subtitle; -@property (nonatomic,copy) NSString *adTitle; -@property (nonatomic,copy) NSString *url; -@property (nonatomic,copy) NSString *source; - - -@property (nonatomic,copy) NSString *TAGS; -@property (nonatomic,copy) NSString *TAG; -/** - * 大图样式 - */ -@property (nonatomic,copy)NSNumber *imgType; -@property (nonatomic,strong)NSArray *specialextra; - - -@property (nonatomic,copy) NSString *boardid; -@property (nonatomic,copy) NSString *commentid; -@property (nonatomic,copy)NSNumber *speciallogo; -@property (nonatomic,copy) NSString *specialtip; -@property (nonatomic,copy) NSString *specialadlogo; - -@property (nonatomic,copy) NSString *pixel; -@property (nonatomic,strong)NSArray *applist; - -@property (nonatomic,copy) NSString *wap_portal; -@property (nonatomic,copy) NSString *live_info; -@property (nonatomic,copy) NSString *ads; -@property (nonatomic,copy) NSString *videosource; - -@property (nonatomic, assign) CGFloat cellHeight; -+ (instancetype)newsModelWithDict:(NSDictionary *)dict; - -@end diff --git a/TTNews/Classes/News/Controller/SXNewsEntity.m b/TTNews/Classes/News/Controller/SXNewsEntity.m deleted file mode 100755 index 6a28ae8..0000000 --- a/TTNews/Classes/News/Controller/SXNewsEntity.m +++ /dev/null @@ -1,62 +0,0 @@ -// -// SXNewsEntity.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved.// - -#import "SXNewsEntity.h" -#import -#import "SinglePictureNewsTableViewCell.h" -#import "MultiPictureTableViewCell.h" - -@implementation SXNewsEntity - -+ (instancetype)newsModelWithDict:(NSDictionary *)dict -{ - SXNewsEntity *model = [[self alloc]init]; - - [model setValuesForKeysWithDictionary:dict]; - [model setValuesForKeysWithDictionary:dict]; -// if (model.hasHead && model.photosetID) { -// model.cellName = @"TopImageCell"; -// }else if (model.hasHead){ -// model.cellName = @"TopTxtCell"; -// }else if (model.imgType){ -// model.cellName = @"BigImageCell"; - - - CGFloat kScreenWidth = [UIScreen mainScreen].bounds.size.width; - CGFloat horizontalMargin = 10; - CGFloat verticalMargin = 15; - CGFloat controlMargin = 5; - CGFloat titleLabelHeight = 19.5; - CGFloat descLabelHeight = 31; - CGFloat commentLabelHeight = 13.5; - - if (model.imgextra.count == 2) { - model.cellHeight = verticalMargin + titleLabelHeight + horizontalMargin + ((kScreenWidth - 4 *horizontalMargin)/3)*3/4 + controlMargin + commentLabelHeight + controlMargin; - } else { - model.cellHeight = verticalMargin + titleLabelHeight + controlMargin + descLabelHeight + controlMargin + commentLabelHeight + controlMargin; - } - - - return model; -} - --(void)setImgextra:(NSArray *)imgextra { - CGFloat kScreenWidth = [UIScreen mainScreen].bounds.size.width; - CGFloat horizontalMargin = 10; - CGFloat verticalMargin = 15; - CGFloat controlMargin = 5; - CGFloat titleLabelHeight = 19.5; - CGFloat descLabelHeight = 31; - CGFloat commentLabelHeight = 13.5; - - if (self.imgextra.count == 2) { - self.cellHeight = verticalMargin + titleLabelHeight + horizontalMargin + ((kScreenWidth - 4 *horizontalMargin)/3)*3/4 + controlMargin + commentLabelHeight + controlMargin; - } else { - self.cellHeight = verticalMargin + titleLabelHeight + controlMargin + descLabelHeight + controlMargin + commentLabelHeight + controlMargin; - } -} -@end diff --git a/TTNews/Classes/News/Controller/ShowMultiPictureViewController.h b/TTNews/Classes/News/Controller/ShowMultiPictureViewController.h deleted file mode 100644 index 4c969f3..0000000 --- a/TTNews/Classes/News/Controller/ShowMultiPictureViewController.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// ShowMultiPictureViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/31. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface ShowMultiPictureViewController : UIViewController - -@property (nonatomic, strong) NSArray *imageUrls; -@property (nonatomic, strong) NSString *text; - -@end diff --git a/TTNews/Classes/News/Controller/ShowMultiPictureViewController.m b/TTNews/Classes/News/Controller/ShowMultiPictureViewController.m deleted file mode 100644 index 0907ed7..0000000 --- a/TTNews/Classes/News/Controller/ShowMultiPictureViewController.m +++ /dev/null @@ -1,109 +0,0 @@ -// -// ShowMultiPictureViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/31. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "ShowMultiPictureViewController.h" -#import -#import "TTConst.h" -#import - -@interface ShowMultiPictureViewController () - -@property (nonatomic, weak) UITextView *textView; -@property (nonatomic, weak) UIButton *backButton; - -@end - -@implementation ShowMultiPictureViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - self.view.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x343434, 0xfafafa); - [self setupScrollView]; - [self setupTextView]; - [self setupBackButton]; -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - self.navigationController.navigationBar.hidden =YES; - -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - self.navigationController.navigationBar.hidden = NO; - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -#pragma mark --private method--初始化scrollView --(void)setupScrollView { - self.automaticallyAdjustsScrollViewInsets = NO; - UIScrollView *scrollView = [[UIScrollView alloc] init]; - scrollView.frame = self.view.frame; - scrollView.backgroundColor = [UIColor blackColor]; - scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*self.imageUrls.count, 0); - scrollView.pagingEnabled = YES; - for (NSInteger i = 0; i - -@interface TTHeaderNews : NSObject - -@property(nonatomic, copy) NSString *ctime; -@property(nonatomic, copy) NSString *title; -@property(nonatomic, copy) NSString *desc; -@property(nonatomic, copy) NSString *picUrl; -@property(nonatomic, copy) NSString *url; - -@end diff --git a/TTNews/Classes/News/Model/TTHeaderNews.m b/TTNews/Classes/News/Model/TTHeaderNews.m deleted file mode 100644 index fdea1e5..0000000 --- a/TTNews/Classes/News/Model/TTHeaderNews.m +++ /dev/null @@ -1,17 +0,0 @@ -// -// TTHeaderNews.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/29. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTHeaderNews.h" -#import -@implementation TTHeaderNews -+(NSDictionary *)mj_replacedKeyFromPropertyName { - return @{@"desc":@"description", - }; -} - -@end diff --git a/TTNews/Classes/News/Model/TTNormalNews.h b/TTNews/Classes/News/Model/TTNormalNews.h deleted file mode 100644 index c82ea59..0000000 --- a/TTNews/Classes/News/Model/TTNormalNews.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// NormalNews.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -#import - -@interface TTNormalNews : NSObject - -typedef NS_ENUM(NSUInteger, NormalNewsType) { - NormalNewsTypeNoPicture = 1, - NormalNewsTypeSigalPicture = 2, - NormalNewsTypeMultiPicture = 3,//图片大于等于三张 -}; - -@property (nonatomic, copy) NSString *channelId; -@property (nonatomic, copy) NSString *desc;//简介 -@property (nonatomic, strong) NSArray *imageurls; -@property (nonatomic, copy) NSString *link; -@property (nonatomic, copy) NSString *pubDate;//发布日期 -@property (nonatomic, copy) NSString *source; -@property (nonatomic, copy) NSString *title; -@property (nonatomic, assign) NSInteger allPages; - -//自己定义的变量 -@property (nonatomic, assign) NormalNewsType normalNewsType; -@property (nonatomic, assign) NSInteger createdtime;//发布日期 -@property (nonatomic, assign) CGFloat cellHeight; - -@end diff --git a/TTNews/Classes/News/Model/TTNormalNews.m b/TTNews/Classes/News/Model/TTNormalNews.m deleted file mode 100644 index 229ef32..0000000 --- a/TTNews/Classes/News/Model/TTNormalNews.m +++ /dev/null @@ -1,44 +0,0 @@ - -// -// NormalNews.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTNormalNews.h" - -@implementation TTNormalNews - --(void)setPubDate:(NSString *)pubDate { - _pubDate = pubDate; - _createdtime = [[[pubDate stringByReplacingOccurrencesOfString:@"-" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""] stringByReplacingOccurrencesOfString:@":" withString:@""].integerValue; -} - --(void)setImageurls:(NSArray *)imageurls { - _imageurls = imageurls; - - CGFloat kScreenWidth = [UIScreen mainScreen].bounds.size.width; - CGFloat horizontalMargin = 10; - CGFloat verticalMargin = 15; - CGFloat controlMargin = 5; - CGFloat titleLabelHeight = 19.5; - CGFloat descLabelHeight = 31; - CGFloat commentLabelHeight = 13.5; - - if (imageurls.count>=3) { - self.normalNewsType = NormalNewsTypeMultiPicture; - self.cellHeight = verticalMargin + titleLabelHeight + horizontalMargin + ((kScreenWidth - 4 *horizontalMargin)/3)*3/4 + controlMargin + commentLabelHeight + controlMargin; - } else if (imageurls.count==0) { - self.normalNewsType = NormalNewsTypeNoPicture; - self.cellHeight = verticalMargin + titleLabelHeight + controlMargin + descLabelHeight + controlMargin + commentLabelHeight + controlMargin; - } else { - self.normalNewsType = NormalNewsTypeSigalPicture; - self.cellHeight = verticalMargin + titleLabelHeight + controlMargin + descLabelHeight + controlMargin + commentLabelHeight + controlMargin; - } - - -} - -@end diff --git a/TTNews/Classes/News/View/BigPictureTableViewCell.h b/TTNews/Classes/News/View/BigPictureTableViewCell.h deleted file mode 100644 index 6d0fba2..0000000 --- a/TTNews/Classes/News/View/BigPictureTableViewCell.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// BigPictureTableViewCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/9/19. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface BigPictureTableViewCell : UITableViewCell - -@property (weak, nonatomic) IBOutlet UILabel *LblTitleLabel; -@property (weak, nonatomic) IBOutlet UIImageView *imgIcon; -@property (weak, nonatomic) IBOutlet UILabel *subTitleLabel; -@end diff --git a/TTNews/Classes/News/View/BigPictureTableViewCell.m b/TTNews/Classes/News/View/BigPictureTableViewCell.m deleted file mode 100644 index 15b1919..0000000 --- a/TTNews/Classes/News/View/BigPictureTableViewCell.m +++ /dev/null @@ -1,24 +0,0 @@ -// -// BigPictureTableViewCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/9/19. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "BigPictureTableViewCell.h" - -@implementation BigPictureTableViewCell - -- (void)awakeFromNib { - [super awakeFromNib]; - // Initialization code -} - -- (void)setSelected:(BOOL)selected animated:(BOOL)animated { - [super setSelected:selected animated:animated]; - - // Configure the view for the selected state -} - -@end diff --git a/TTNews/Classes/News/View/BigPictureTableViewCell.xib b/TTNews/Classes/News/View/BigPictureTableViewCell.xib deleted file mode 100644 index b59738c..0000000 --- a/TTNews/Classes/News/View/BigPictureTableViewCell.xib +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/News/View/ChannelCollectionViewCell.h b/TTNews/Classes/News/View/ChannelCollectionViewCell.h deleted file mode 100644 index ac8bc0d..0000000 --- a/TTNews/Classes/News/View/ChannelCollectionViewCell.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// ChannelCollectionViewCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/30. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@protocol ChannelCollectionViewCellDelegate - -- (void)didLongPressAChannelCell; -- (void)deleteTheCellAtIndexPath:(NSIndexPath*)indexPath; - -@end - -@interface ChannelCollectionViewCell : UICollectionViewCell - -@property (nonatomic, copy) NSString *channelName; -@property (weak, nonatomic) IBOutlet UIButton *deleteButton; -@property (nonatomic, strong) NSIndexPath *theIndexPath; -@property (nonatomic, weak) id delegate; - -- (void)startShake; -- (void)stopShake; - -@end - diff --git a/TTNews/Classes/News/View/ChannelCollectionViewCell.m b/TTNews/Classes/News/View/ChannelCollectionViewCell.m deleted file mode 100644 index 14318a9..0000000 --- a/TTNews/Classes/News/View/ChannelCollectionViewCell.m +++ /dev/null @@ -1,78 +0,0 @@ -// -// ChannelCollectionViewCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/30. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "ChannelCollectionViewCell.h" -#import - -@interface ChannelCollectionViewCell() -@property (weak, nonatomic) IBOutlet UILabel *channelNameLabel; - -@end - -static NSString * const kShakeAnimationKey = @"kCollectionViewCellShake"; - -@implementation ChannelCollectionViewCell - -- (void)awakeFromNib { - [super awakeFromNib]; - - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - - self.channelNameLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); -} - --(void)longPress { - if([self.delegate respondsToSelector:@selector(didLongPressAChannelCell)]) { - [self.delegate didLongPressAChannelCell]; - } -} - -- (IBAction)DeleteTheChannel:(id)sender { - if ([self.delegate respondsToSelector:@selector(deleteTheCellAtIndexPath:)]) {//判断代理对象是否有removeTheCell方法; - [self.delegate deleteTheCellAtIndexPath:self.theIndexPath]; - } - self.deleteButton.hidden = YES; -} - --(void)setTheIndexPath:(NSIndexPath *)theIndexPath { - UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress)]; - recognizer.minimumPressDuration = 0.5; - [self addGestureRecognizer:recognizer]; - _theIndexPath = theIndexPath; -} - --(void)setChannelName:(NSString *)channelName { - _channelName = channelName; - self.deleteButton.hidden = YES; - self.channelNameLabel.text = channelName; -} - -- (void)startShake { - CGPoint point = self.contentView.center; - CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; - animation.keyPath = @"position"; - NSValue *value1=[NSValue valueWithCGPoint:CGPointMake(point.x - 1, point.y+1)]; - NSValue *value2=[NSValue valueWithCGPoint:CGPointMake(point.x + 2, point.y+2)]; - NSValue *value3=[NSValue valueWithCGPoint:CGPointMake(point.x - 1, point.y+1)]; - NSValue *value4=[NSValue valueWithCGPoint:CGPointMake(point.x + 1, point.y-1)]; - NSValue *value5=[NSValue valueWithCGPoint:CGPointMake(point.x - 2, point.y-1)]; - animation.values=@[value1,value2,value3,value4,value5]; - animation.repeatCount = MAXFLOAT; -// animation.removedOnCompletion = NO; -// animation.fillMode = kCAFillModeRemoved; - animation.duration = 0.25; - animation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; -// animation.delegate=self; - [self.contentView.layer addAnimation:animation forKey:kShakeAnimationKey]; -} - -- (void)stopShake { - [self.contentView.layer removeAnimationForKey:kShakeAnimationKey]; -} - -@end diff --git a/TTNews/Classes/News/View/ChannelCollectionViewCell.xib b/TTNews/Classes/News/View/ChannelCollectionViewCell.xib deleted file mode 100644 index 554791b..0000000 --- a/TTNews/Classes/News/View/ChannelCollectionViewCell.xib +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/News/View/ChannelsSectionHeaderView.h b/TTNews/Classes/News/View/ChannelsSectionHeaderView.h deleted file mode 100644 index eea6456..0000000 --- a/TTNews/Classes/News/View/ChannelsSectionHeaderView.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// ChannelsSectionHeaderView.h -// TTNews -// -// Created by 瑞文戴尔 on 16/5/1. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface ChannelsSectionHeaderView : UICollectionReusableView - -@property (nonatomic, weak) UILabel *titleLabel; - -@end diff --git a/TTNews/Classes/News/View/ChannelsSectionHeaderView.m b/TTNews/Classes/News/View/ChannelsSectionHeaderView.m deleted file mode 100644 index 8946228..0000000 --- a/TTNews/Classes/News/View/ChannelsSectionHeaderView.m +++ /dev/null @@ -1,32 +0,0 @@ -// -// ChannelsSectionHeaderView.m -// TTNews -// -// Created by 瑞文戴尔 on 16/5/1. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "ChannelsSectionHeaderView.h" -#import -@interface ChannelsSectionHeaderView() - -@end - -@implementation ChannelsSectionHeaderView - --(instancetype)initWithFrame:(CGRect)frame { - if (self == [super initWithFrame:frame]) { - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x343434, 0xfafafa); - - CGFloat margin = 15; - UILabel *label = [[UILabel alloc] init]; - self.titleLabel = label; - label.frame = CGRectMake(margin, 0, [UIScreen mainScreen].bounds.size.width - 2*margin, frame.size.height); - [self addSubview:label]; - } - return self; -} - - - -@end diff --git a/TTNews/Classes/News/View/MultiPictureTableViewCell.h b/TTNews/Classes/News/View/MultiPictureTableViewCell.h deleted file mode 100644 index 9603209..0000000 --- a/TTNews/Classes/News/View/MultiPictureTableViewCell.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// MultiPictureTableViewCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/27. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface MultiPictureTableViewCell : UITableViewCell - -@property (nonatomic, strong) NSArray *imageUrls; -@property (nonatomic, copy) NSString *theTitle; -@property (nonatomic, copy) NSString *iconImage; - -@end diff --git a/TTNews/Classes/News/View/MultiPictureTableViewCell.m b/TTNews/Classes/News/View/MultiPictureTableViewCell.m deleted file mode 100644 index e804fe1..0000000 --- a/TTNews/Classes/News/View/MultiPictureTableViewCell.m +++ /dev/null @@ -1,56 +0,0 @@ -// -// MultiPictureTableViewCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/27. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "MultiPictureTableViewCell.h" -#import "TTNormalNews.h" -#import -#import - -@interface MultiPictureTableViewCell() - -@property (weak, nonatomic) IBOutlet UILabel *newsTittleLabel; -@property (weak, nonatomic) IBOutlet UIImageView *imageView1; -@property (weak, nonatomic) IBOutlet UIImageView *imageView2; -@property (weak, nonatomic) IBOutlet UIImageView *imageView3; -@property (weak, nonatomic) IBOutlet UILabel *pictureCountLabel; -@property (weak, nonatomic) IBOutlet UILabel *commentCountLabel; -@property (weak, nonatomic) IBOutlet UIView *separatorLine; - -@end - -@implementation MultiPictureTableViewCell - -- (void)awakeFromNib { - [super awakeFromNib]; - - self.selectionStyle = UITableViewCellSelectionStyleNone; - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - - self.newsTittleLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.commentCountLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.pictureCountLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.separatorLine.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); -} - --(void)setImageUrls:(NSArray *)imageUrls{ - _imageUrls = imageUrls; - [self.imageView1 sd_setImageWithURL:imageUrls[0]]; - [self.imageView2 sd_setImageWithURL:imageUrls[1]]; - [self.imageView3 sd_setImageWithURL:imageUrls[2]]; - self.pictureCountLabel.text = [NSString stringWithFormat:@"%ld图 ", (unsigned long)imageUrls.count]; - self.commentCountLabel.text = [NSString stringWithFormat:@"%d评论", arc4random()%1000]; -} - --(void)setTheTitle:(NSString *)theTitle { - _theTitle = theTitle; - self.newsTittleLabel.text = theTitle; - -} - - -@end diff --git a/TTNews/Classes/News/View/MultiPictureTableViewCell.xib b/TTNews/Classes/News/View/MultiPictureTableViewCell.xib deleted file mode 100644 index 7af15fb..0000000 --- a/TTNews/Classes/News/View/MultiPictureTableViewCell.xib +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/News/View/NoPictureNewsTableViewCell.h b/TTNews/Classes/News/View/NoPictureNewsTableViewCell.h deleted file mode 100644 index 6e23160..0000000 --- a/TTNews/Classes/News/View/NoPictureNewsTableViewCell.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// NoPictureNewsTableViewCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/14. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface NoPictureNewsTableViewCell : UITableViewCell - -@property (nonatomic, copy) NSString *titleText; -@property (nonatomic, copy) NSString *contentText; - - -@end diff --git a/TTNews/Classes/News/View/NoPictureNewsTableViewCell.m b/TTNews/Classes/News/View/NoPictureNewsTableViewCell.m deleted file mode 100644 index 6ce966a..0000000 --- a/TTNews/Classes/News/View/NoPictureNewsTableViewCell.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// NoPictureNewsTableViewCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/14. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "NoPictureNewsTableViewCell.h" -#import - -@interface NoPictureNewsTableViewCell() - -@property (weak, nonatomic) IBOutlet UILabel *newsTitleLabel; -@property (weak, nonatomic) IBOutlet UILabel *contentLabel; -@property (weak, nonatomic) IBOutlet UILabel *commentCountLabel; -@property (weak, nonatomic) IBOutlet UIView *separatorLine; - -@end - -@implementation NoPictureNewsTableViewCell - -- (void)awakeFromNib { - [super awakeFromNib]; - self.commentCountLabel.text = [NSString stringWithFormat:@"%d评论",arc4random()%1000]; - self.selectionStyle = UITableViewCellSelectionStyleNone; - // Initialization code - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - self.newsTitleLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.contentLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.commentCountLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.separatorLine.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); - -} - -- (void)setSelected:(BOOL)selected animated:(BOOL)animated { - [super setSelected:selected animated:animated]; - // Configure the view for the selected state -} - --(void)setTitleText:(NSString *)titleText { - _titleText = titleText; - self.newsTitleLabel.text = titleText; -} - --(void)setContentText:(NSString *)contentText { - _contentText = contentText; - self.newsTitleLabel.text = contentText; -} - - - -@end diff --git a/TTNews/Classes/News/View/NoPictureNewsTableViewCell.xib b/TTNews/Classes/News/View/NoPictureNewsTableViewCell.xib deleted file mode 100644 index 499a8fc..0000000 --- a/TTNews/Classes/News/View/NoPictureNewsTableViewCell.xib +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/News/View/SinglePictureNewsTableViewCell.h b/TTNews/Classes/News/View/SinglePictureNewsTableViewCell.h deleted file mode 100644 index fc9468b..0000000 --- a/TTNews/Classes/News/View/SinglePictureNewsTableViewCell.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// SinglePictureNewsTableViewCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/26. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -#import "SXNewsEntity.h" -@interface SinglePictureNewsTableViewCell : UITableViewCell - -@property (nonatomic, strong) NSArray *pictureArray; -@property (nonatomic, copy) NSString *imageUrl; -@property (nonatomic, copy) NSString *contentTittle; -@property (nonatomic, copy) NSString *desc; -@property (weak, nonatomic) IBOutlet UIImageView *iconImage; -@property (weak, nonatomic) IBOutlet UILabel *LblTitleLabel; -@property (weak, nonatomic) IBOutlet UILabel *subTitleLabel; - -@end diff --git a/TTNews/Classes/News/View/SinglePictureNewsTableViewCell.m b/TTNews/Classes/News/View/SinglePictureNewsTableViewCell.m deleted file mode 100644 index 2be42ca..0000000 --- a/TTNews/Classes/News/View/SinglePictureNewsTableViewCell.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// SinglePictureNewsTableViewCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/26. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "SinglePictureNewsTableViewCell.h" -#import -#import - -@interface SinglePictureNewsTableViewCell() -@property (weak, nonatomic) IBOutlet UIImageView *pictureImageView; -@property (weak, nonatomic) IBOutlet UILabel *newsTittleLabel; -@property (weak, nonatomic) IBOutlet UILabel *commentCount; -@property (weak, nonatomic) IBOutlet UILabel *descLabel; - -@property (weak, nonatomic) IBOutlet UIView *separatorLine; -@end -@implementation SinglePictureNewsTableViewCell - -- (void)awakeFromNib { - [super awakeFromNib]; - - self.commentCount.text = [NSString stringWithFormat:@"%d评论",arc4random()%1000]; - self.selectionStyle = UITableViewCellSelectionStyleNone; - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - self.newsTittleLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.commentCount.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.descLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.separatorLine.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); - -} - - --(void)setImageUrl:(NSString *)imageUrl { - _imageUrl = imageUrl; - [self.pictureImageView sd_setImageWithURL:[NSURL URLWithString:imageUrl]]; -} - --(void)setContentTittle:(NSString *)contentTittle { - _contentTittle = contentTittle; - self.newsTittleLabel.text = contentTittle; -} - --(void)setDesc:(NSString *)desc { - _desc = desc; - self.descLabel.text = desc; -} - - -@end diff --git a/TTNews/Classes/News/View/SinglePictureNewsTableViewCell.xib b/TTNews/Classes/News/View/SinglePictureNewsTableViewCell.xib deleted file mode 100644 index f3eacf6..0000000 --- a/TTNews/Classes/News/View/SinglePictureNewsTableViewCell.xib +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/News/View/TTImageCyclePlayView.h b/TTNews/Classes/News/View/TTImageCyclePlayView.h deleted file mode 100644 index b29aeb4..0000000 --- a/TTNews/Classes/News/View/TTImageCyclePlayView.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// TTImageCyclePlayView.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/28. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@protocol TTImageCyclePlayViewDelegate -@optional - -- (void)clickCurrentImageViewInImageCyclePlay; - -@end - -@interface TTImageCyclePlayView : UIView - -@property (nonatomic, strong) NSArray *imageUrls; -@property (nonatomic, strong) NSArray *titles; -@property (nonatomic, assign) NSInteger currentMiddleImageViewIndex; -@property (nonatomic, weak) id delegate; - --(instancetype)initWithFrame:(CGRect)frame; -- (void)updateImageViewsAndTitleLabel; -- (void)addTimer; -- (void)removeTimer; - -@end diff --git a/TTNews/Classes/News/View/TTImageCyclePlayView.m b/TTNews/Classes/News/View/TTImageCyclePlayView.m deleted file mode 100644 index c8bffc5..0000000 --- a/TTNews/Classes/News/View/TTImageCyclePlayView.m +++ /dev/null @@ -1,174 +0,0 @@ -// -// TTImageCyclePlayView.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/28. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTImageCyclePlayView.h" -#import -#import - -@interface TTImageCyclePlayView () - -@property (nonatomic, weak) UIScrollView *scrollView; -@property (nonatomic, weak) UIImageView *leftImageView; -@property (nonatomic, weak) UIImageView *middleImageView; -@property (nonatomic, weak) UIImageView *rightImageView; -@property (nonatomic, weak) UIView *bottomContianerView; -@property (nonatomic, weak) UIPageControl *pageControl; -@property (nonatomic, strong) NSTimer *timer; -@property (nonatomic, weak) UILabel *titleLabel; - -@end - -@implementation TTImageCyclePlayView - -#pragma mark 初始化View -- (instancetype)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { - [self initialization]; - } - return self; -} - -#pragma mark 初始化子控件 -- (void)initialization { - //初始化scrollView - UIScrollView *scrollView = [[UIScrollView alloc] init]; - self.scrollView = scrollView; - scrollView.frame = self.bounds; - scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*3, 0); - scrollView.contentOffset = CGPointMake(scrollView.frame.size.width, 0); - scrollView.delegate = self; - scrollView.pagingEnabled = YES; - scrollView.showsHorizontalScrollIndicator = NO; - [self addSubview:scrollView]; - - //初始化scrollView上的左中右三张imageView, - for (NSInteger i = 0; i < 3; i++) { - UIImageView *imageView = [[UIImageView alloc] init]; - imageView.frame = CGRectMake(i*scrollView.frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height); - [scrollView addSubview:imageView]; - if (i==0) { - self.leftImageView = imageView; - } else if (i==1) { - self.middleImageView = imageView; - imageView.userInteractionEnabled = YES; - [imageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickMiddleImageView)]]; - - } else if (i==2) { - self.rightImageView = imageView; - } - } - - //初始化View下方的存放titleLabel和pageControl的contianerView - CGFloat kBottomContianerViewlHeight = 30; - UIView *bottomContianerView = [[UIView alloc] init]; - self.bottomContianerView = bottomContianerView; - bottomContianerView.frame = CGRectMake(0, self.bounds.size.height -kBottomContianerViewlHeight, self.bounds.size.width, kBottomContianerViewlHeight); - bottomContianerView.backgroundColor = [UIColor darkGrayColor]; - bottomContianerView.alpha = 0.8; - [self addSubview:bottomContianerView]; - - //初始化pageControl - UIPageControl *pageControl = [[UIPageControl alloc] init]; - self.pageControl = pageControl; - pageControl.numberOfPages = 5; - pageControl.currentPage = 0; - CGFloat kPageControlWidth = [pageControl sizeForNumberOfPages:5].width; - CGFloat margin = 10; - pageControl.frame = CGRectMake(bottomContianerView.frame.size.width - kPageControlWidth - margin, 0, kPageControlWidth, bottomContianerView.frame.size.height); - - [bottomContianerView addSubview:pageControl]; - - //初始化titleLabel - UILabel *titleLabel = [[UILabel alloc] init]; - self.titleLabel = titleLabel; - self.titleLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - - titleLabel.frame = CGRectMake(0, 0, bottomContianerView.frame.size.width - kPageControlWidth - 2*margin, bottomContianerView.frame.size.height); - titleLabel.textAlignment = NSTextAlignmentLeft; - titleLabel.textColor = [UIColor whiteColor]; - titleLabel.backgroundColor = [UIColor darkGrayColor]; - titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; - [bottomContianerView addSubview:titleLabel]; - -} - -#pragma mark UIScrollViewDelegate scrollView开始拖动 -- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - [self removeTimer]; -} - -#pragma mark UIScrollViewDelegate scrollView将要停止拖动(即手指将离开屏幕) -- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { - [self addTimer]; -} - -#pragma mark UIScrollViewDelegate scrollView将要停止减速 -- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - if (scrollView == self.scrollView) { - if (self.imageUrls == nil || self.imageUrls.count == 0 || self.titles == nil || self.titles.count == 0) return; - - if (scrollView.contentOffset.x>=scrollView.frame.size.width) {//向右滑动 - self.currentMiddleImageViewIndex = (self.currentMiddleImageViewIndex + 1)%self.imageUrls.count; - } else {//向左滑动 - self.currentMiddleImageViewIndex = (self.currentMiddleImageViewIndex - 1 + self.imageUrls.count)%self.imageUrls.count; - } - - [self updateImageViewsAndTitleLabel]; - } -} - -#pragma mark 数据刷新后更新imageView和TitleLabel -- (void)updateImageViewsAndTitleLabel { - if (self.imageUrls == nil || self.imageUrls.count == 0 || self.titles == nil || self.titles.count == 0) return; - - NSInteger leftIndex = (self.currentMiddleImageViewIndex - 1 + self.imageUrls.count)%self.imageUrls.count; - [self.leftImageView sd_setImageWithURL:[NSURL URLWithString:self.imageUrls[leftIndex]]]; - - self.titleLabel.text = [NSString stringWithFormat:@" %@", self.titles[self.currentMiddleImageViewIndex]]; - [self.middleImageView sd_setImageWithURL:[NSURL URLWithString:self.imageUrls[self.currentMiddleImageViewIndex]]]; - - NSInteger rightIndex = (self.currentMiddleImageViewIndex + 1)%self.imageUrls.count; - [self.rightImageView sd_setImageWithURL:[NSURL URLWithString:self.imageUrls[rightIndex]]]; - - self.pageControl.numberOfPages = self.imageUrls.count; - self.pageControl.currentPage = self.currentMiddleImageViewIndex; - //重新设置scrollView的contentOffset,即将滑动后的imageView显示在最中间 - [self.scrollView setContentOffset:CGPointMake(self.scrollView.frame.size.width, 0)]; -} - -#pragma mark 添加定时器 -- (void)addTimer { - self.timer = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(nextNews) userInfo:nil repeats:YES]; - [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode]; - -} - -#pragma mark 移除定时器 -- (void)removeTimer{ - [self.timer invalidate]; - self.timer = nil; -} - -#pragma mark scrollView轮播到下一个ImageView -- (void)nextNews { - [UIView animateWithDuration:0.5 animations:^{ - [self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x+[UIScreen mainScreen].bounds.size.width, 0)]; - }]; - [self scrollViewDidEndDecelerating:self.scrollView]; -} - -#pragma mark 点击了中间的ImageView即当前显示的ImageView -- (void)clickMiddleImageView { - if ([self.delegate respondsToSelector:@selector(clickCurrentImageViewInImageCyclePlay)]) { - [self.delegate clickCurrentImageViewInImageCyclePlay]; - } -} - - - -@end diff --git a/TTNews/Classes/News/View/TTTopChannelContianerView.h b/TTNews/Classes/News/View/TTTopChannelContianerView.h deleted file mode 100644 index d15ef14..0000000 --- a/TTNews/Classes/News/View/TTTopChannelContianerView.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// TTTopChannelContianerView.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/29. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -//这是新闻首页上方的新闻频道选择的scrollview - -#import - -@protocol TTTopChannelContianerViewDelegate - -@optional - -- (void)showOrHiddenAddChannelsCollectionView:(UIButton *)button; -- (void)chooseChannelWithIndex:(NSInteger)index; - -@end - -@interface TTTopChannelContianerView : UIView - -- (instancetype)initWithFrame:(CGRect)frame; -- (void)addAChannelButtonWithChannelName:(NSString *)channelName; -- (void)selectChannelButtonWithIndex:(NSInteger)index; -- (void)deleteChannelButtonWithIndex:(NSInteger)index; - -//- (void)didShowEditChannelView:(BOOL)value; - -@property (nonatomic, strong) NSArray *channelNameArray; -@property (nonatomic, weak) UIScrollView *scrollView; -//@property (nonatomic, weak) UIButton *addButton; -@property (nonatomic, weak) id delegate; - -@end diff --git a/TTNews/Classes/News/View/TTTopChannelContianerView.m b/TTNews/Classes/News/View/TTTopChannelContianerView.m deleted file mode 100644 index b05b450..0000000 --- a/TTNews/Classes/News/View/TTTopChannelContianerView.m +++ /dev/null @@ -1,211 +0,0 @@ -// -// TTTopChannelContianerView.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/29. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTTopChannelContianerView.h" -#import - -@interface TTTopChannelContianerView() - -@property (nonatomic, weak) UIButton *lastSelectedButton; -@property (nonatomic, weak) UIView *indicatorView; - -@end - -static CGFloat kTitleLabelNorimalFont = 13; -static CGFloat kTitleLabelSelectedFont = 16; -static CGFloat kAddChannelWidth = 30; -static CGFloat kSliderViewWidth = 20; -static CGFloat buttonWidth = 65; - -@implementation TTTopChannelContianerView - -#pragma mark 初始化View -- (instancetype)initWithFrame:(CGRect)frame { - if (self= [super initWithFrame:frame]) { - [self initialization]; - } - return self; -} - -#pragma mark channelNameArray的setter方法,channelNameArray -- (void)setChannelNameArray:(NSArray *)channelNameArray { - _channelNameArray = channelNameArray; -// CGFloat buttonWidth = self.scrollView.frame.size.width/5; - self.scrollView.contentSize = CGSizeMake(buttonWidth * channelNameArray.count, 0); - for (NSInteger i = 0; i < channelNameArray.count; i++) { - UIButton *button = [self createChannelButton]; - button.frame = CGRectMake(i*buttonWidth, 0, buttonWidth, self.frame.size.height); - [button setTitle:channelNameArray[i] forState:UIControlStateNormal]; - [self.scrollView addSubview:button]; - } - - //默认选中第三个channelButton,因为scrollView的subview含有indicatorView,所以第三个按钮对应scrollView的subview的index是3 - [self clickChannelButton:self.scrollView.subviews[3]]; -} - -#pragma mark 初始化子控件 -- (void)initialization { - self.alpha = 0.9; - - //初始化scrollView - UIScrollView *scrollView = [self createScrollView]; - self.scrollView = scrollView; - [self addSubview:self.scrollView]; - - //初始化scrollView右侧的显示阴影效果的imageView -// [self addSubview:[self createSliderView]]; - - //初始化被选中channelButton的红线,也就是indicatorView - UIView *indicatorView = [self createIndicatorView]; - self.indicatorView = indicatorView; - [self.scrollView addSubview:self.indicatorView]; - - //初始化右侧的加号button -// UIButton *button = [self createTheAddButton]; -// self.addButton = button; -// [self addSubview:self.addButton]; - -} - -#pragma mark 创建容纳channelButton的ScrollView -- (UIScrollView *)createScrollView { - UIScrollView *scrollView = [[UIScrollView alloc] init]; - self.scrollView = scrollView; - scrollView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, self.frame.size.height); - scrollView.showsHorizontalScrollIndicator = NO; - scrollView.showsVerticalScrollIndicator = NO; - return scrollView; -} - -//#pragma mark 创建右侧的加号Button -//- (UIButton *)createTheAddButton { -// -// UIButton *addChannelButton =[UIButton buttonWithType:UIButtonTypeCustom]; -// addChannelButton.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x343434, 0xfafafa); -// self.addButton = addChannelButton; -// [addChannelButton setImage:[UIImage imageNamed:@"home_header_add_slim"] forState:UIControlStateNormal]; -// addChannelButton.frame = CGRectMake([UIScreen mainScreen].bounds.size.width - kAddChannelWidth, 0, kAddChannelWidth, kAddChannelWidth); -// [addChannelButton addTarget:self action:@selector(clickAddButton:) forControlEvents:UIControlEventTouchUpInside]; -// return addChannelButton; -//} - -#pragma mark 初始化scrollView右侧的显示阴影效果的imageView -- (UIView *)createSliderView { - UIImageView *slideView = [[UIImageView alloc] init]; - slideView.frame = CGRectMake(self.frame.size.width - kSliderViewWidth -kAddChannelWidth, 0, kSliderViewWidth, self.frame.size.height); - slideView.alpha = 0.9; - slideView.image = [UIImage imageNamed:@"slidetab_mask"]; - return slideView; -} - -#pragma mark 创建被选中channelButton的红线,也就是indicatorView -- (UIView *)createIndicatorView { - UIView *indicatorView = [[UIView alloc] init]; - indicatorView.backgroundColor = [UIColor colorWithRed:243/255.0 green:75/255.0 blue:80/255.0 alpha:1.0]; - [self addSubview:indicatorView]; - return indicatorView; -} - -#pragma mark 创建ChannelButton -- (UIButton *)createChannelButton{ - UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; - [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; - button.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x949494, 0xfafafa); - [button setTitleColor:[UIColor colorWithRed:243/255.0 green:75/255.0 blue:80/255.0 alpha:1.0] forState:UIControlStateDisabled]; - [button.titleLabel setFont:[UIFont systemFontOfSize:kTitleLabelNorimalFont]]; - [button addTarget:self action:@selector(clickChannelButton:) forControlEvents:UIControlEventTouchUpInside]; - [button layoutIfNeeded]; - return button; -} - -#pragma mark 选择了某个ChannelButton -- (void)clickChannelButton:(UIButton *)sender { - self.lastSelectedButton.titleLabel.font = [UIFont systemFontOfSize:kTitleLabelNorimalFont]; - self.lastSelectedButton.enabled = YES; - self.lastSelectedButton = sender; - self.lastSelectedButton.enabled = NO; - //选中的标签要居中,也就是scrollView的offset.x加屏幕的一半要等于标签的中心 - CGFloat newOffsetX = sender.center.x - [UIScreen mainScreen].bounds.size.width*0.5; - if (newOffsetX < 0) { - newOffsetX = 0; - } - if (newOffsetX > self.scrollView.contentSize.width - self.scrollView.frame.size.width) { - newOffsetX = self.scrollView.contentSize.width - self.scrollView.frame.size.width; - } - [UIView animateWithDuration:0.25 animations:^{ - [sender.titleLabel setFont:[UIFont systemFontOfSize:kTitleLabelSelectedFont]]; - [sender layoutIfNeeded]; - [self.scrollView setContentOffset:CGPointMake(newOffsetX, 0)]; - //indicatorView宽度会比titleLabel宽20,centerX与titleLabel相同 - self.indicatorView.frame = CGRectMake(sender.frame.origin.x + sender.titleLabel.frame.origin.x - 10, self.frame.size.height - 2, sender.titleLabel.frame.size.width + 20, 2); - }]; - - //因为subviews包含indicatorView,所以index需要减1 - NSInteger index = [self.scrollView.subviews indexOfObject:sender] - 1; - if ([self.delegate respondsToSelector:@selector(chooseChannelWithIndex:)]) { - [self.delegate chooseChannelWithIndex:index]; - } -} - -#pragma mark 选中某个ChannelButton -- (void)selectChannelButtonWithIndex:(NSInteger)index { - self.indicatorView.hidden = NO; - //因为subviews包含indicatorView,所以index需要加1 - [self clickChannelButton:self.scrollView.subviews[index+1]]; -} - -#pragma mark 删除某个ChannelButton -- (void)deleteChannelButtonWithIndex:(NSInteger)index { - //删除index对应的button,因为subviews包含indicatorView,所以index需要加1 - NSInteger realIndex= index + 1; - [self.scrollView.subviews[realIndex] removeFromSuperview]; - //后面的button的x向左移动buuton宽度的距离 - for (NSInteger i = realIndex; i - -@interface TopPictureTableViewCell : UITableViewCell -@property (weak, nonatomic) IBOutlet UIImageView *imgIcon; -@property (weak, nonatomic) IBOutlet UILabel *LblTitleLabel; - -@end diff --git a/TTNews/Classes/News/View/TopPictureTableViewCell.m b/TTNews/Classes/News/View/TopPictureTableViewCell.m deleted file mode 100644 index e3cfd79..0000000 --- a/TTNews/Classes/News/View/TopPictureTableViewCell.m +++ /dev/null @@ -1,24 +0,0 @@ -// -// TopPictureTableViewCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/9/19. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TopPictureTableViewCell.h" - -@implementation TopPictureTableViewCell - -- (void)awakeFromNib { - [super awakeFromNib]; - // Initialization code -} - -- (void)setSelected:(BOOL)selected animated:(BOOL)animated { - [super setSelected:selected animated:animated]; - - // Configure the view for the selected state -} - -@end diff --git a/TTNews/Classes/News/View/TopPictureTableViewCell.xib b/TTNews/Classes/News/View/TopPictureTableViewCell.xib deleted file mode 100644 index b402f6d..0000000 --- a/TTNews/Classes/News/View/TopPictureTableViewCell.xib +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/News/View/TopTextTableViewCell.h b/TTNews/Classes/News/View/TopTextTableViewCell.h deleted file mode 100644 index 5c3085b..0000000 --- a/TTNews/Classes/News/View/TopTextTableViewCell.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// TopTextTableViewCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/9/19. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TopTextTableViewCell : UITableViewCell -@property (weak, nonatomic) IBOutlet UILabel *LblTitleLabel; -@property (weak, nonatomic) IBOutlet UIImageView *imgIcon; - -@end diff --git a/TTNews/Classes/News/View/TopTextTableViewCell.m b/TTNews/Classes/News/View/TopTextTableViewCell.m deleted file mode 100644 index 6e1dd1e..0000000 --- a/TTNews/Classes/News/View/TopTextTableViewCell.m +++ /dev/null @@ -1,24 +0,0 @@ -// -// TopTextTableViewCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/9/19. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TopTextTableViewCell.h" - -@implementation TopTextTableViewCell - -- (void)awakeFromNib { - [super awakeFromNib]; - // Initialization code -} - -- (void)setSelected:(BOOL)selected animated:(BOOL)animated { - [super setSelected:selected animated:animated]; - - // Configure the view for the selected state -} - -@end diff --git a/TTNews/Classes/News/View/TopTextTableViewCell.xib b/TTNews/Classes/News/View/TopTextTableViewCell.xib deleted file mode 100644 index 52330d2..0000000 --- a/TTNews/Classes/News/View/TopTextTableViewCell.xib +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/Other/Category/Foundation+Log.m b/TTNews/Classes/Other/Category/Foundation+Log.m deleted file mode 100644 index 285bb6e..0000000 --- a/TTNews/Classes/Other/Category/Foundation+Log.m +++ /dev/null @@ -1,64 +0,0 @@ -// -// Foundation+Log.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - - -#import - -@implementation NSDictionary (Log) -- (NSString *)descriptionWithLocale:(id)locale -{ - NSMutableString *string = [NSMutableString string]; - - // 开头有个{ - [string appendString:@"{\n"]; - - // 遍历所有的键值对 - [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { - [string appendFormat:@"\t%@", key]; - [string appendString:@" : "]; - [string appendFormat:@"%@,\n", obj]; - }]; - - // 结尾有个} - [string appendString:@"}"]; - - // 查找最后一个逗号 - NSRange range = [string rangeOfString:@"," options:NSBackwardsSearch]; - if (range.location != NSNotFound) - [string deleteCharactersInRange:range]; - - return string; -} -@end - -@implementation NSArray (Log) - -- (NSString *)descriptionWithLocale:(id)locale -{ - NSMutableString *string = [NSMutableString string]; - - // 开头有个[ - [string appendString:@"[\n"]; - - // 遍历所有的元素 - [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - [string appendFormat:@"\t%@,\n", obj]; - }]; - - // 结尾有个] - [string appendString:@"]"]; - - // 查找最后一个逗号 - NSRange range = [string rangeOfString:@"," options:NSBackwardsSearch]; - if (range.location != NSNotFound) - [string deleteCharactersInRange:range]; - - return string; -} - -@end diff --git a/TTNews/Classes/Other/Category/NSDate+Extension.h b/TTNews/Classes/Other/Category/NSDate+Extension.h deleted file mode 100644 index 5a198e9..0000000 --- a/TTNews/Classes/Other/Category/NSDate+Extension.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// NSDate+Extension.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface NSDate (Extension) -/** - * 比较from和self的时间差值 - */ -- (NSDateComponents *)deltaFrom:(NSDate *)from; - -/** - * 是否为今年 - */ -- (BOOL)isThisYear; - -/** - * 是否为今天 - */ -- (BOOL)isToday; - -/** - * 是否为昨天 - */ -- (BOOL)isYesterday; -@end diff --git a/TTNews/Classes/Other/Category/NSDate+Extension.m b/TTNews/Classes/Other/Category/NSDate+Extension.m deleted file mode 100644 index 0e13767..0000000 --- a/TTNews/Classes/Other/Category/NSDate+Extension.m +++ /dev/null @@ -1,80 +0,0 @@ -// -// NSDate+Extension.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "NSDate+Extension.h" - -@implementation NSDate (Extension) -- (NSDateComponents *)deltaFrom:(NSDate *)from -{ - // 日历 - NSCalendar *calendar = [NSCalendar currentCalendar]; - - // 比较时间 - NSCalendarUnit unit = NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; - - return [calendar components:unit fromDate:from toDate:self options:0]; -} - -- (BOOL)isThisYear -{ - // 日历 - NSCalendar *calendar = [NSCalendar currentCalendar]; - - NSInteger nowYear = [calendar component:NSCalendarUnitYear fromDate:[NSDate date]]; - NSInteger selfYear = [calendar component:NSCalendarUnitYear fromDate:self]; - - return nowYear == selfYear; -} - -//- (BOOL)isToday -//{ -// // 日历 -// NSCalendar *calendar = [NSCalendar currentCalendar]; -// -// NSCalendarUnit unit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay; -// -// NSDateComponents *nowCmps = [calendar components:unit fromDate:[NSDate date]]; -// NSDateComponents *selfCmps = [calendar components:unit fromDate:self]; -// -// return nowCmps.year == selfCmps.year -// && nowCmps.month == selfCmps.month -// && nowCmps.day == selfCmps.day; -//} - -- (BOOL)isToday -{ - NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; - fmt.dateFormat = @"yyyy-MM-dd"; - - NSString *nowString = [fmt stringFromDate:[NSDate date]]; - NSString *selfString = [fmt stringFromDate:self]; - - return [nowString isEqualToString:selfString]; -} - -- (BOOL)isYesterday -{ - // 2014-12-31 23:59:59 -> 2014-12-31 - // 2015-01-01 00:00:01 -> 2015-01-01 - - // 日期格式化类 - NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; - fmt.dateFormat = @"yyyy-MM-dd"; - - NSDate *nowDate = [fmt dateFromString:[fmt stringFromDate:[NSDate date]]]; - NSDate *selfDate = [fmt dateFromString:[fmt stringFromDate:self]]; - - NSCalendar *calendar = [NSCalendar currentCalendar]; - NSDateComponents *cmps = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:selfDate toDate:nowDate options:0]; - - return cmps.year == 0 - && cmps.month == 0 - && cmps.day == 1; -} - -@end diff --git a/TTNews/Classes/Other/Category/UIBarButtonItem+Extension.h b/TTNews/Classes/Other/Category/UIBarButtonItem+Extension.h deleted file mode 100644 index fb05ff3..0000000 --- a/TTNews/Classes/Other/Category/UIBarButtonItem+Extension.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// UIBarButtonItem+Extension.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface UIBarButtonItem (Extension) -+ (instancetype)itemWithImage:(NSString *)image highImage:(NSString *)highImage target:(id)target action:(SEL)action; -@end diff --git a/TTNews/Classes/Other/Category/UIBarButtonItem+Extension.m b/TTNews/Classes/Other/Category/UIBarButtonItem+Extension.m deleted file mode 100644 index 9381855..0000000 --- a/TTNews/Classes/Other/Category/UIBarButtonItem+Extension.m +++ /dev/null @@ -1,21 +0,0 @@ -// -// UIBarButtonItem+Extension.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "UIBarButtonItem+Extension.h" - -@implementation UIBarButtonItem (Extension) -+ (instancetype)itemWithImage:(NSString *)image highImage:(NSString *)highImage target:(id)target action:(SEL)action -{ - UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; - [button setBackgroundImage:[UIImage imageNamed:image] forState:UIControlStateNormal]; - [button setBackgroundImage:[UIImage imageNamed:highImage] forState:UIControlStateHighlighted]; - button.frame = CGRectMake(0, 0, button.currentBackgroundImage.size.width, button.currentBackgroundImage.size.height); - [button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; - return [[self alloc] initWithCustomView:button]; -} -@end diff --git a/TTNews/Classes/Other/Category/UIImage+Extension.h b/TTNews/Classes/Other/Category/UIImage+Extension.h deleted file mode 100644 index 28d99f4..0000000 --- a/TTNews/Classes/Other/Category/UIImage+Extension.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// UIImage+Extension.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface UIImage (Extension) -/** - * 圆形图片 - */ -- (UIImage *)circleImage; -@end diff --git a/TTNews/Classes/Other/Category/UIImage+Extension.m b/TTNews/Classes/Other/Category/UIImage+Extension.m deleted file mode 100644 index 959d03a..0000000 --- a/TTNews/Classes/Other/Category/UIImage+Extension.m +++ /dev/null @@ -1,36 +0,0 @@ -// -// UIImage+Extension.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "UIImage+Extension.h" - -@implementation UIImage (Extension) -- (UIImage *)circleImage -{ - // NO代表透明 - UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0); - - // 获得上下文 - CGContextRef context = UIGraphicsGetCurrentContext(); - - // 添加一个圆 - CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height); - CGContextAddEllipseInRect(context, rect); - - // 裁剪 - CGContextClip(context); - - // 将图片画上去 - [self drawInRect:rect]; - - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - - return image; -} -@end diff --git a/TTNews/Classes/Other/Category/UIImageView+Extension.h b/TTNews/Classes/Other/Category/UIImageView+Extension.h deleted file mode 100644 index 48e5916..0000000 --- a/TTNews/Classes/Other/Category/UIImageView+Extension.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// UIImageView+Extension.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface UIImageView (Extension) - --(void)TT_setImageWithURL:(NSURL *)url; - --(void)TT_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(void (^)(UIImage *image, NSError *error))complete; - -- (void)TT_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(NSInteger)options progress:(void (^)(NSInteger receivedSize, NSInteger expectedSize))progressBlock completed:(void (^)(UIImage *image, NSError *error))complete; - -- (void)TT_setImageAfterClickWithURL:(NSURL *)url; - - -@end diff --git a/TTNews/Classes/Other/Category/UIImageView+Extension.m b/TTNews/Classes/Other/Category/UIImageView+Extension.m deleted file mode 100644 index 084cfcd..0000000 --- a/TTNews/Classes/Other/Category/UIImageView+Extension.m +++ /dev/null @@ -1,66 +0,0 @@ -// -// UIImageView+Extension.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "UIImageView+Extension.h" -#import -#import "TTConst.h" -#import "TTJudgeNetworking.h" - -@implementation UIImageView (Extension) - --(void)TT_setImageWithURL:(NSURL *)url { - if ([self currentImageDownLoadMode] == NO || url == nil) { - self.image = [UIImage imageNamed:@"allplaceholderImage"]; - return; - } - [self sd_setImageWithURL:url]; -} - --(void)TT_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(void (^)(UIImage *image, NSError *error))complete { - if ([self currentImageDownLoadMode] == NO || url == nil) { - NSError *error = [NSError errorWithDomain:@"example.com" code:500 userInfo:nil]; - complete(placeholder,error); - return; - } - - [self sd_setImageWithURL:url placeholderImage:placeholder completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - complete(image,error); - }]; -} - -- (void)TT_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(NSInteger)options progress:(void (^)(NSInteger receivedSize, NSInteger expectedSize))progressBlock completed:(void (^)(UIImage *image, NSError *error))complete { - if ([self currentImageDownLoadMode] == NO || url == nil) { - NSError *error = [NSError errorWithDomain:@"example.com" code:500 userInfo:nil]; - progressBlock(100, 100); - complete(placeholder,error); - return; - } - - [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:^(NSInteger receivedSize, NSInteger expectedSize) { - progressBlock(receivedSize, expectedSize); - } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - complete(image,error); - }]; - -} - -- (void)TT_setImageAfterClickWithURL:(NSURL *)url { - [self sd_setImageWithURL:url]; -} - -- (BOOL)currentImageDownLoadMode { - BOOL isDownLoadNoImageIn3G = [[NSUserDefaults standardUserDefaults] boolForKey:IsDownLoadNoImageIn3GKey]; - if (isDownLoadNoImageIn3G == YES && [TTJudgeNetworking currentNetworkingType] != NetworkingTypeWiFi) {//在设置中选择了智能无图,并且当前网络非Wifi - return NO; - } - return YES; -} - - - -@end diff --git a/TTNews/Classes/Other/Category/UIView+Extension.h b/TTNews/Classes/Other/Category/UIView+Extension.h deleted file mode 100644 index e4dbaa8..0000000 --- a/TTNews/Classes/Other/Category/UIView+Extension.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIView+Extension.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface UIView (Extension) -@property (nonatomic, assign) CGFloat x; -@property (nonatomic, assign) CGFloat y; -@property (nonatomic, assign) CGFloat width; -@property (nonatomic, assign) CGFloat height; -//@property (nonatomic, assign) CGRect frame; -@property (nonatomic, assign) CGSize size; - -@end diff --git a/TTNews/Classes/Other/Category/UIView+Extension.m b/TTNews/Classes/Other/Category/UIView+Extension.m deleted file mode 100644 index 6c528ee..0000000 --- a/TTNews/Classes/Other/Category/UIView+Extension.m +++ /dev/null @@ -1,63 +0,0 @@ -// -// UIView+Extension.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "UIView+Extension.h" - -@implementation UIView (Extension) - --(CGFloat)x { - return self.frame.origin.x; -} - --(void)setX:(CGFloat)x { - CGRect frame = self.frame; - frame.origin.x = x; - self.frame = frame; -} - -- (CGFloat)y { - return self.frame.origin.y; -} - -- (void)setY:(CGFloat)y { - CGRect frame = self.frame; - frame.origin.y = y; - self.frame = frame; -} - -- (CGFloat)width { - return self.frame.size.width; -} - -- (void)setWidth:(CGFloat)width { - CGRect frame = self.frame; - frame.size.width = width; - self.frame = frame; -} - -- (CGFloat)height { - return self.frame.size.height; -} - -- (void)setHeight:(CGFloat)height { - CGRect frame = self.frame; - frame.size.height = height; - self.frame = frame; -} - --(CGSize)size { - return self.frame.size; -} - --(void)setSize:(CGSize)size { - CGRect frame = self.frame; - frame.size = size; - self.frame = frame; -} - -@end diff --git a/TTNews/Classes/Other/Http/TTConst.h b/TTNews/Classes/Other/Http/TTConst.h deleted file mode 100644 index 3367d10..0000000 --- a/TTNews/Classes/Other/Http/TTConst.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// TTConst.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/30. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -#import - -UIKIT_EXTERN CGFloat const cellMargin; -UIKIT_EXTERN CGFloat const cellTextY; -UIKIT_EXTERN CGFloat const cellBottomBarHeight; -UIKIT_EXTERN CGFloat const cellTopCommentTopLabelHeight; - -extern NSString * const IsShakeCanChangeSkinKey; -extern NSString * const IsDownLoadNoImageIn3GKey; -extern NSString * const UserNameKey; -extern NSString * const UserSignatureKey; diff --git a/TTNews/Classes/Other/Http/TTConst.m b/TTNews/Classes/Other/Http/TTConst.m deleted file mode 100644 index 5f15a2c..0000000 --- a/TTNews/Classes/Other/Http/TTConst.m +++ /dev/null @@ -1,17 +0,0 @@ -// -// TTConst.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/30. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// -#import - -CGFloat const cellMargin = 10; -CGFloat const cellTextY = 55; -CGFloat const cellBottomBarHeight = 40; -CGFloat const cellTopCommentTopLabelHeight = 16; -NSString * const IsShakeCanChangeSkinKey = @"IsShakeCanChangeSkinKey"; -NSString * const IsDownLoadNoImageIn3GKey = @"IsDownLoadNoImageIn3GKey"; -NSString * const UserNameKey = @"UserNameKey"; -NSString * const UserSignatureKey = @"UserSignatureKey"; diff --git a/TTNews/Classes/Other/Http/TTDataTool.h b/TTNews/Classes/Other/Http/TTDataTool.h deleted file mode 100644 index edaaaef..0000000 --- a/TTNews/Classes/Other/Http/TTDataTool.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// TTDataTool.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/7. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@class TTVideo; -@class TTHeaderNews; -@class TTVideoFetchDataParameter; -@class TTPictureFetchDataParameter; -@class TTNormalNewsFetchDataParameter; -@interface TTDataTool : NSObject - -+(void)videoWithParameters:(TTVideoFetchDataParameter *)videoParameters success:(void (^)(NSArray *array, NSString *maxtime))success failure:(void (^)(NSError *error))failure; - -+(void)pictureWithParameters:(TTPictureFetchDataParameter *)pictureParameters success:(void (^)(NSArray *array, NSString *maxtime))success failure:(void (^)(NSError *error))failure; - - -+(void)TTNormalNewsWithParameters:(TTNormalNewsFetchDataParameter *)normalNewsParameters success:(void (^)(NSMutableArray *array))success failure:(void (^)(NSError *error))failure; - -+(void)TTHeaderNewsFromServerOrCacheWithMaxTTHeaderNews:(TTHeaderNews *)headerNews success:(void (^)(NSMutableArray *array))success failure:(void (^)(NSError *error))failure; - -+(void)VideoCommentsWithParameters:(NSMutableDictionary *)parameters success:(void (^)(NSDictionary *responseObject))success failure:(void (^) (NSError *error))failure; - -+(void)PictureCommentsWithParameters:(NSMutableDictionary *)parameters success:(void (^)(NSDictionary * responseObject))success failure:(void (^) (NSError *error))failure; - -+(void)deletePartOfCacheInSqlite; - - -@end diff --git a/TTNews/Classes/Other/Http/TTDataTool.m b/TTNews/Classes/Other/Http/TTDataTool.m deleted file mode 100644 index 7456b1f..0000000 --- a/TTNews/Classes/Other/Http/TTDataTool.m +++ /dev/null @@ -1,443 +0,0 @@ -// -// TTDataTool.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/7. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -//缓存数据的工具类 - -#import "TTDataTool.h" -#import "TTVideo.h" -#import "TTPicture.h" -#import "TTHeaderNews.h" -#import "TTConst.h" -#import "TTVideoFetchDataParameter.h" -#import "TTPictureFetchDataParameter.h" -#import "TTNormalNewsFetchDataParameter.h" -#import "TTNormalNews.h" -#import "TTJudgeNetworking.h" -#import "TTVideoComment.h" -#import -#import -#import -#import "TTNetworkManager.h" -#import - -@interface TTDataTool() - -@end - -@implementation TTDataTool -static FMDatabaseQueue *_queue; -static NSString * const apikey = @"8b72ce2839d6eea0869b4c2c60d2a449"; - -+(void)initialize { - NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"data.sqlite"]; -// NSLog(@"%@",path); - _queue = [FMDatabaseQueue databaseQueueWithPath:path]; - - [_queue inDatabase:^(FMDatabase *db) { - [db executeUpdate:@"create table if not exists table_video(id integer primary key autoincrement, idstr text, time integer, video blob);"]; - - [db executeUpdate:@"create table if not exists table_picture(id integer primary key autoincrement, idstr text, time integer, picture blob);"]; - - [db executeUpdate:@"create table if not exists table_ttheadernews(id integer primary key autoincrement, title text, url text, desc text, picUrl text, ctime text);"]; - - [db executeUpdate:@"create table if not exists table_normalnews(id integer primary key autoincrement, channelid text, title text, imageurls blob, desc text, link text, pubdate text, createdtime integer, source text);"]; - [db executeUpdate:@"create table if not exists table_videocomment(id integer primary key autoincrement, idstr text, page integer, hotcommentarray blob, latestcommentarray blob, total integer);"]; - }]; -} - -+(void)videoWithParameters:(TTVideoFetchDataParameter *)videoParameters success:(void (^)(NSArray *array, NSString *maxtime))success failure:(void (^)(NSError *error))failure { - - NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; - parameters[@"a"] = @"list"; - parameters[@"c"] = @"data"; - parameters[@"type"] = @(41); - parameters[@"page"] = @(videoParameters.page); - if (videoParameters.maxtime) { - parameters[@"maxtime"] = videoParameters.maxtime; - } - - [[TTNetworkManager shareManager] Get:@"http://api.budejie.com/api/api_open.php" Parameters:parameters Success:^(NSURLSessionDataTask *task, id responseObject) { - NSArray *array = [TTVideo mj_objectArrayWithKeyValuesArray:responseObject[@"list"]]; - NSString *maxTime = responseObject[@"info"][@"maxtime"]; - for (TTVideo *video in array) { - video.maxtime = maxTime; - } - success(array,maxTime); - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self addVideoArray:array]; - }); - - } Failure:^(NSError *error) { - videoParameters.recentTime = nil; - videoParameters.remoteTime = nil; - NSMutableArray *videoArray = [self selectDataFromCacheWithVideoParameters:videoParameters]; - if (videoArray.count>0) { - TTVideo *lastVideo = videoArray.lastObject; - NSString *maxtime = lastVideo.maxtime; - success(videoArray, maxtime); - } - success([videoArray copy], @""); - }]; -} - - -+(NSMutableArray *)selectDataFromCacheWithVideoParameters:(TTVideoFetchDataParameter *)parameters { - __block NSMutableArray *videoArray = nil; - - [_queue inDatabase:^(FMDatabase *db) { - videoArray = [NSMutableArray array]; - FMResultSet *result = nil; - - if (parameters.recentTime) {//时间更大,代表消息发布越靠后,因为时间是按real来储存的 - NSInteger time = [[[parameters.recentTime stringByReplacingOccurrencesOfString:@"-" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""] stringByReplacingOccurrencesOfString:@":" withString:@""].integerValue; - - result = [db executeQuery:@"select * from table_video where time > ? order by time desc limit 0,20;", @(time)]; - - } - - if(parameters.remoteTime) { - NSInteger time = [[[parameters.remoteTime stringByReplacingOccurrencesOfString:@"-" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""] stringByReplacingOccurrencesOfString:@":" withString:@""].integerValue; - result = [db executeQuery:@"select * from table_video where time < ? order by time desc limit 0,20;",@(time)]; - } - - if (parameters.remoteTime==nil &¶meters.recentTime==nil){ - result = [db executeQuery:@"select * from table_video order by time desc limit 0,20;"]; - - } - - while (result.next) { - NSData *data = [result dataForColumn:@"video"]; - TTVideo *video = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - [videoArray addObject:video]; - } - - }]; - return videoArray; -} - -+(void)addVideoArray:(NSArray *)videoArray { - for (TTVideo *video in videoArray) { - [self addVideo:video]; - } -} - -+(void)addVideo:(TTVideo *)video { - [_queue inDatabase:^(FMDatabase *db) { - NSString *idstr = video.ID; - FMResultSet *result = nil; - NSString *querySql = [NSString stringWithFormat:@"SELECT * FROM table_video WHERE idstr = '%@';",idstr]; - result = [db executeQuery:querySql]; - if (result.next==NO) {//不存在此条数据 - NSString *string = video.created_at; - NSInteger time = [[[string stringByReplacingOccurrencesOfString:@"-" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""] stringByReplacingOccurrencesOfString:@":" withString:@""].integerValue; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:video]; - [db executeUpdate:@"insert into table_video (idstr,time,video) values(?,?,?);", idstr, @(time), data]; - } - [result close]; - - }]; -} - - -+(void)pictureWithParameters:(TTPictureFetchDataParameter *)pictureParameters success:(void (^)(NSArray *array, NSString *maxtime))success failure:(void (^)(NSError *error))failure { NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; - parameters[@"a"] = @"list"; - parameters[@"c"] = @"data"; - parameters[@"type"] = @(10); - parameters[@"page"] = @(pictureParameters.page); - if (pictureParameters.maxtime) { - parameters[@"maxtime"] = pictureParameters.maxtime; - } - - [[TTNetworkManager shareManager] Get:@"http://api.budejie.com/api/api_open.php" Parameters:parameters Success:^(NSURLSessionDataTask *task, id responseObject) { - - NSArray *array = [TTPicture mj_objectArrayWithKeyValuesArray:responseObject[@"list"]]; - NSString *maxTime = responseObject[@"info"][@"maxtime"]; - for (TTPicture *picture in array) { - picture.maxtime = maxTime; - } - success(array,maxTime); - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self addPictureArray:array]; - }); - - } Failure:^(NSError *error) { - pictureParameters.recentTime = nil; - pictureParameters.remoteTime = nil; - NSMutableArray *pictureArray = [self selectDataFromCacheWithPictureParameters:pictureParameters]; - if (pictureArray.count>0) { - TTPicture *lastPicture = pictureArray.lastObject; - NSString *maxtime = lastPicture.maxtime; - success([pictureArray copy], maxtime); - } - success([pictureArray copy], @""); - }]; -} - - -+(NSMutableArray *)selectDataFromCacheWithPictureParameters:(TTPictureFetchDataParameter *)parameters { - __block NSMutableArray *pictureArray = nil; - - [_queue inDatabase:^(FMDatabase *db) { - pictureArray = [NSMutableArray array]; - FMResultSet *result = nil; - - if (parameters.recentTime) {//时间更大,代表消息发布越靠后,因为时间是按real来储存的 - NSInteger time = [[[parameters.recentTime stringByReplacingOccurrencesOfString:@"-" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""] stringByReplacingOccurrencesOfString:@":" withString:@""].integerValue; - - result = [db executeQuery:@"select * from table_picture where time > ? order by time desc limit 0,20;", @(time)]; - - } - - if(parameters.remoteTime) { - NSInteger time = [[[parameters.remoteTime stringByReplacingOccurrencesOfString:@"-" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""] stringByReplacingOccurrencesOfString:@":" withString:@""].integerValue; - result = [db executeQuery:@"select * from table_picture where time < ? order by time desc limit 0,20;",@(time)]; - } - - if (parameters.remoteTime==nil && parameters.recentTime==nil){ - result = [db executeQuery:@"select * from table_picture order by time desc limit 0,20;"]; - - } - - while (result.next) { - NSData *data = [result dataForColumn:@"picture"]; - TTPicture *picture = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - [pictureArray addObject:picture]; - } - - }]; - return pictureArray; - -} - -+(void)addPicture:(TTPicture *)picture { - [_queue inDatabase:^(FMDatabase *db) { - NSString *idstr = picture.ID; - FMResultSet *result = nil; - NSString *querySql = [NSString stringWithFormat:@"SELECT * FROM table_picture WHERE idstr = '%@';",idstr]; - result = [db executeQuery:querySql]; - if (result.next==NO) {//不存在此条数据 - NSString *string = picture.created_at; - NSInteger time = [[[string stringByReplacingOccurrencesOfString:@"-" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""] stringByReplacingOccurrencesOfString:@":" withString:@""].integerValue; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:picture]; - [db executeUpdate:@"insert into table_picture (idstr,time,picture) values(?,?,?);", idstr, @(time), data]; - } - [result close]; - }]; -} - -+ (void)addPictureArray:(NSArray *)pictureArray { - for (TTPicture *picture in pictureArray) { - [self addPicture:picture]; - } -} - -+ (void)TTHeaderNewsFromServerOrCacheWithMaxTTHeaderNews:(TTHeaderNews *)headerNews success:(void (^)(NSMutableArray *array))success failure:(void (^)(NSError *error))failure { - - AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; - [manager.requestSerializer setValue:@"8b72ce2839d6eea0869b4c2c60d2a449" forHTTPHeaderField:@"apikey"]; - NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; - parameters[@"num"] = @20; - [manager GET:@"http://apis.baidu.com/txapi/social/social" parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { - NSMutableArray *headerNewsArray = [TTHeaderNews mj_objectArrayWithKeyValuesArray:responseObject[@"newslist"]]; - NSMutableArray *temmArray = [NSMutableArray array]; - for (TTHeaderNews *headerNews in headerNewsArray) { - if (![headerNews.picUrl isEqualToString:@""]) { - [temmArray addObject:headerNews]; - } - if (temmArray.count>=5) break; - } - - success(temmArray); - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self addTTHeaderNewsArray:[temmArray copy]]; - }); - } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { - failure(error); - [SVProgressHUD showErrorWithStatus:@"无网络连接"]; - NSMutableArray *array = [self TTHeaderNewsFromCacheWithMaxTTHeaderNews:headerNews]; - success(array); - NSLog(@"%@",error); - }]; - -// [[TTNetworkManager shareManager] Get2:@"http://apis.baidu.com/songshuxiansheng/news/news" Parameters:nil Success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) { -// NSMutableArray *headerNewsArray = [TTHeaderNews mj_objectArrayWithKeyValuesArray:responseObject[@"retData"]]; -// NSArray *temmArray = [headerNewsArray copy]; -// for (TTHeaderNews *headerNews in temmArray) { -// if ([headerNews.image_url isEqualToString:@""]) { -// [headerNewsArray removeObject:headerNews]; -// } -// } -// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ -// [self addTTHeaderNewsArray:[headerNewsArray copy]]; -// }); -// -// success(headerNewsArray); -// } Failure:^(NSError *error) { -// [SVProgressHUD showErrorWithStatus:@"无网络连接"]; -// NSMutableArray *array = [self TTHeaderNewsFromCacheWithMaxTTHeaderNews:headerNews]; -// success(array); -// NSLog(@"%@",error); -// }]; -} - -+(NSMutableArray *)TTHeaderNewsFromCacheWithMaxTTHeaderNews:(TTHeaderNews *)headerNews { - __block NSMutableArray *headerNewsArray; - [_queue inDatabase:^(FMDatabase *db) { - headerNewsArray = [NSMutableArray array]; - FMResultSet *result = nil; - result = [db executeQuery:@"select * from table_ttheadernews order by id desc limit 0,5"]; - while (result.next) { - TTHeaderNews *headerNews = [[TTHeaderNews alloc] init]; - headerNews.title = [result stringForColumn:@"title"]; - headerNews.url = [result stringForColumn:@"url"]; - headerNews.picUrl = [result stringForColumn:@"picUrl"]; - headerNews.desc = [result stringForColumn:@"desc"]; - headerNews.ctime = [result stringForColumn:@"ctime"]; - - [headerNewsArray addObject:headerNews]; - } - }]; - return headerNewsArray; -} - - -+(void)addTTHeaderNews:(TTHeaderNews *)news { - [_queue inDatabase:^(FMDatabase *db) { - NSString *url = news.url; - FMResultSet *result = nil; - NSString *querySql = [NSString stringWithFormat:@"SELECT * FROM table_ttheadernews WHERE url = '%@';",url]; - result = [db executeQuery:querySql]; - if (result.next==NO) {//不存在此条数据 - [db executeUpdate:@"insert into table_ttheadernews (title ,url, desc, picUrl, ctime) values(?,?,?,?,?);",news.title, news.url, news.desc, news.picUrl, news.ctime]; - } - [result close]; - }]; -} - -+(void)addTTHeaderNewsArray:(NSArray *)headerNewsArray { - for (TTHeaderNews *news in headerNewsArray) { - [self addTTHeaderNews:news]; - } -} - -+(void)TTNormalNewsWithParameters:(TTNormalNewsFetchDataParameter *)normalNewsParameters success:(void (^)(NSMutableArray *array))success failure:(void (^)(NSError *error))failure { - NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; - parameters[@"channelid"] = normalNewsParameters.channelId; - parameters[@"channelName"] = [normalNewsParameters.channelName stringByAppendingString:@"最新"]; - parameters[@"title"] = normalNewsParameters.title; - parameters[@"page"] = @(normalNewsParameters.page); - - [[TTNetworkManager shareManager] Get2:@"http://apis.baidu.com/showapi_open_bus/channel_news/search_news" Parameters:parameters Success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) { - NSMutableArray *pictureArray = [TTNormalNews mj_objectArrayWithKeyValuesArray:responseObject[@"showapi_res_body"][@"pagebean"][@"contentlist"]]; - for (TTNormalNews *news in pictureArray) { - news.allPages = [responseObject[@"showapi_res_body"][@"pagebean"][@"allPages"] integerValue]; - } - - success(pictureArray); - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self addTTNormalNewsArray:pictureArray]; - }); - } Failure:^(NSError *error) { - failure(error); - TTNormalNewsFetchDataParameter *tempParameters = [[TTNormalNewsFetchDataParameter alloc] init]; - tempParameters.channelId = normalNewsParameters.channelId; - NSMutableArray *tempCacheArray = [self selectDataFromTTNormalNewsCacheWithParameters:tempParameters]; - success(tempCacheArray); - }]; -} - - -+(NSMutableArray *)selectDataFromTTNormalNewsCacheWithParameters:(TTNormalNewsFetchDataParameter *)parameters { - __block NSMutableArray *newsArray = nil; - - [_queue inDatabase:^(FMDatabase *db) { - newsArray = [NSMutableArray array]; - FMResultSet *result = nil; - if (parameters.recentTime!=0) {//时间更大,代表消息发布越靠后,因为时间是按real来储存的 - NSInteger time = parameters.recentTime; - NSString *sql = [NSString stringWithFormat:@"select * from table_normalnews where createdtime > %@ and channelid = '%@' order by createdtime desc limit 0,20;", @(time),parameters.channelId]; - - result = [db executeQuery:sql]; - - } - - if(parameters.remoteTime!=0) { - NSInteger time = parameters.remoteTime; - NSString *sql = [NSString stringWithFormat:@"select * from table_normalnews where createdtime < %@ and channelid = '%@' order by createdtime desc limit 0,20;", @(time),parameters.channelId]; - - result = [db executeQuery:sql]; - } - - if (parameters.remoteTime==0 && parameters.recentTime==0){ - - NSString *sql = [NSString stringWithFormat:@"select * from table_normalnews where channelid = '%@' order by createdtime desc limit 0,20;", parameters.channelId]; - result = [db executeQuery:sql]; - } - - while (result.next) { - TTNormalNews *news = [[TTNormalNews alloc] init]; - news.title = [result stringForColumn:@"title"]; - news.pubDate = [result stringForColumn:@"pubdate"]; - news.createdtime = [result longLongIntForColumn:@"createdtime"]; - - news.imageurls = [NSKeyedUnarchiver unarchiveObjectWithData:[result dataForColumn:@"imageurls"]]; - news.source = [result stringForColumn:@"source"]; - news.desc = [result stringForColumn:@"desc"]; - news.link = [result stringForColumn:@"link"]; - news.channelId = [result stringForColumn:@"channelId"]; - [newsArray addObject:news]; - } - }]; - return newsArray; - -} - - -+(void)addTTNormalNews:(TTNormalNews *)news { - [_queue inDatabase:^(FMDatabase *db) { - FMResultSet *result = nil; - NSString *querySql = [NSString stringWithFormat:@"SELECT * FROM table_normalnews WHERE link = '%@';",news.link]; - result = [db executeQuery:querySql]; - if (result.next==NO) {//不存在此条数据 - NSData *imageurls = [NSKeyedArchiver archivedDataWithRootObject:news.imageurls]; - [db executeUpdate:@"insert into table_normalnews (title , pubdate, createdtime, source, desc, link, imageurls, channelid) values(?,?,?,?,?,?,?,?);",news.title, news.pubDate, @(news.createdtime), news.source, news.desc, news.link, imageurls,news.channelId]; - } - [result close]; - }]; -} - -+(void)addTTNormalNewsArray:(NSMutableArray *)newsArray { - for (TTNormalNews *news in newsArray) { - [self addTTNormalNews:news]; - } -} - -+(void)deletePartOfCacheInSqlite { - [_queue inDatabase:^(FMDatabase *db) { - [db executeUpdate:@"delete from table_video where id > 20"]; - [db executeUpdate:@"delete from table_picture where id > 20"]; - [db executeUpdate:@"delete from table_normalnews where id > 20"]; - [db executeUpdate:@"delete from table_ttheadernews where id > 5"]; - }]; -} - -+(void)VideoCommentsWithParameters:(NSMutableDictionary *)parameters success:(void (^)(NSDictionary *))success failure:(void (^)(NSError *))failure { - [[TTNetworkManager shareManager] Get:@"http://api.budejie.com/api/api_open.php" Parameters:parameters Success:^(NSURLSessionDataTask *task, id responseObject) { - success(responseObject); - } Failure:^(NSError *error) { - failure(error); - }]; -} - -+(void)PictureCommentsWithParameters:(NSMutableDictionary *)parameters success:(void (^)(NSDictionary *))success failure:(void (^)(NSError *))failure { - [[TTNetworkManager shareManager] Get:@"http://api.budejie.com/api/api_open.php" Parameters:parameters Success:^(NSURLSessionDataTask *task, id responseObject) { - success(responseObject); - } Failure:^(NSError *error) { - failure(error); - }]; -} - -@end diff --git a/TTNews/Classes/Other/Http/TTNetworkManager.h b/TTNews/Classes/Other/Http/TTNetworkManager.h deleted file mode 100644 index 4f0b69b..0000000 --- a/TTNews/Classes/Other/Http/TTNetworkManager.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// TTNetworkManager.h -// TTNews -// -// Created by 瑞文戴尔 on 16/8/11. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -typedef void (^Completion)(NSURLSessionDataTask *task ,NSDictionary *responseObject); -typedef void (^Failure)(NSError *error); - -@interface TTNetworkManager : NSObject - -+(TTNetworkManager *)shareManager; - -/** - * 有请求头的get请求 - */ --(void)Get:(NSString *)url Parameters:(NSDictionary *)parameters Success:(Completion)success Failure:(Failure)failure; - -/** - * 有请求头的get请求 - */ --(void)Get2:(NSString *)url Parameters:(NSDictionary *)parameters Success:(Completion)success Failure:(Failure)failure; - -@end diff --git a/TTNews/Classes/Other/Http/TTNetworkManager.m b/TTNews/Classes/Other/Http/TTNetworkManager.m deleted file mode 100644 index 30b5751..0000000 --- a/TTNews/Classes/Other/Http/TTNetworkManager.m +++ /dev/null @@ -1,86 +0,0 @@ -// -// TTNetworkManager.m -// TTNews -// -// Created by 瑞文戴尔 on 16/8/11. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTNetworkManager.h" - -@implementation TTNetworkManager -+(TTNetworkManager *)shareManager { - static TTNetworkManager *manager = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken ,^{ - manager = [[self alloc] init]; - }); - return manager; -} - --(void)Get:(NSString *)url Parameters:(NSDictionary *)parameters Success:(Completion)success Failure:(Failure)failure { - __block NSInteger flag = 0; - __block NSString *theUrl = url; - if (parameters) { - [parameters enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { - if (flag == 0) { - theUrl = [theUrl stringByAppendingString:[NSString stringWithFormat:@"?%@=%@",key,obj]]; - flag = 1; - } else { - theUrl = [theUrl stringByAppendingString:[NSString stringWithFormat:@"&%@=%@",key,obj]]; - } - }]; - } - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:theUrl]]; - NSURLSessionDataTask *task =[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - if (error) { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - return ; - } - NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(task,dict); - }); - - }]; - [task resume]; -} - --(void)Get2:(NSString *)url Parameters:(NSDictionary *)parameters Success:(Completion)success Failure:(Failure)failure{ - __block NSInteger flag = 0; - __block NSString *theUrl = url; - if (parameters) { - [parameters enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { - if (flag == 0) { - theUrl = [theUrl stringByAppendingString:[NSString stringWithFormat:@"?%@=%@",key,obj]]; - flag = 1; - } else { - theUrl = [theUrl stringByAppendingString:[NSString stringWithFormat:@"&%@=%@",key,obj]]; - } - }]; - } - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[theUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; - NSMutableURLRequest *mutableRequest = [request mutableCopy]; - NSString *apikey = @"8b72ce2839d6eea0869b4c2c60d2a449"; - [mutableRequest addValue:apikey forHTTPHeaderField:@"apikey"]; - request = [mutableRequest copy]; - NSURLSessionDataTask *task =[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - if (error) { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - return ; - } -// NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; -// NSLog(@"%@",string); - NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(task,dict); - }); - }]; - [task resume]; -} - -@end diff --git a/TTNews/Classes/Other/Http/TTNormalNewsFetchDataParameter.h b/TTNews/Classes/Other/Http/TTNormalNewsFetchDataParameter.h deleted file mode 100644 index d5c3dcd..0000000 --- a/TTNews/Classes/Other/Http/TTNormalNewsFetchDataParameter.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// TTNormalNewsFetchDataParameter.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/11. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TTNormalNewsFetchDataParameter : NSObject - -@property (nonatomic, assign) NSInteger recentTime;//最新的picture的时间 -@property (nonatomic, assign) NSInteger remoteTime;//最晚的picture的时间 - -@property (nonatomic, copy) NSString *channelId; -@property (nonatomic, copy) NSString *channelName; -@property (nonatomic, copy) NSString *title; -@property (nonatomic, assign) NSInteger page; - -@end diff --git a/TTNews/Classes/Other/Http/TTNormalNewsFetchDataParameter.m b/TTNews/Classes/Other/Http/TTNormalNewsFetchDataParameter.m deleted file mode 100644 index 01e1d1e..0000000 --- a/TTNews/Classes/Other/Http/TTNormalNewsFetchDataParameter.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// TTNormalNewsFetchDataParameter.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/11. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTNormalNewsFetchDataParameter.h" - -@implementation TTNormalNewsFetchDataParameter - -@end diff --git a/TTNews/Classes/Other/Http/TTPictureFetchDataParameter.h b/TTNews/Classes/Other/Http/TTPictureFetchDataParameter.h deleted file mode 100644 index c62a9da..0000000 --- a/TTNews/Classes/Other/Http/TTPictureFetchDataParameter.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// TTPictureFetchDataParameter.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TTPictureFetchDataParameter : NSObject -@property (nonatomic, copy) NSString *recentTime;//最新的picture的时间 - -@property (nonatomic, copy) NSString *remoteTime;//最晚的picture的时间 - -@property (nonatomic, copy) NSString *maxtime; - -@property (nonatomic, assign) NSInteger page; -@end diff --git a/TTNews/Classes/Other/Http/TTPictureFetchDataParameter.m b/TTNews/Classes/Other/Http/TTPictureFetchDataParameter.m deleted file mode 100644 index 38b81fc..0000000 --- a/TTNews/Classes/Other/Http/TTPictureFetchDataParameter.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// TTPictureFetchDataParameter.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTPictureFetchDataParameter.h" - -@implementation TTPictureFetchDataParameter - -@end diff --git a/TTNews/Classes/Other/Http/TTVideoFetchDataParameter.h b/TTNews/Classes/Other/Http/TTVideoFetchDataParameter.h deleted file mode 100644 index 38ce140..0000000 --- a/TTNews/Classes/Other/Http/TTVideoFetchDataParameter.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// TTVideoFetchDataParameter.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/7. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TTVideoFetchDataParameter : NSObject - -@property (nonatomic, copy) NSString *recentTime;//最新的video的时间 - -@property (nonatomic, copy) NSString *remoteTime;//最晚的video的时间 - -@property (nonatomic, copy) NSString *maxtime; - -@property (nonatomic, assign) NSInteger page; - -@end diff --git a/TTNews/Classes/Other/Http/TTVideoFetchDataParameter.m b/TTNews/Classes/Other/Http/TTVideoFetchDataParameter.m deleted file mode 100644 index 27c65aa..0000000 --- a/TTNews/Classes/Other/Http/TTVideoFetchDataParameter.m +++ /dev/null @@ -1,15 +0,0 @@ -// -// TTVideoFetchDataParameter.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/7. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTVideoFetchDataParameter.h" - -@implementation TTVideoFetchDataParameter - - - -@end diff --git a/TTNews/Classes/Other/Main/AppDelegate.h b/TTNews/Classes/Other/Main/AppDelegate.h deleted file mode 100644 index 8f257ab..0000000 --- a/TTNews/Classes/Other/Main/AppDelegate.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// AppDelegate.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface AppDelegate : UIResponder - -@property (strong, nonatomic) UIWindow *window; - - -@end - diff --git a/TTNews/Classes/Other/Main/AppDelegate.m b/TTNews/Classes/Other/Main/AppDelegate.m deleted file mode 100644 index 574e81d..0000000 --- a/TTNews/Classes/Other/Main/AppDelegate.m +++ /dev/null @@ -1,77 +0,0 @@ -// -// AppDelegate.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/24. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "AppDelegate.h" -#import "TTTabBarController.h" -#import "TTConst.h" - -@interface AppDelegate () - -@end - -@implementation AppDelegate - - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [self setupUserDefaults]; - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - self.window.rootViewController = [[TTTabBarController alloc] init]; - [self.window makeKeyAndVisible]; - return YES; -} - --(void)setupUserDefaults { - - BOOL isShakeCanChangeSkin = [[NSUserDefaults standardUserDefaults] boolForKey:IsShakeCanChangeSkinKey]; - if (!isShakeCanChangeSkin) { - [[NSUserDefaults standardUserDefaults] setObject:@(NO) forKey:IsShakeCanChangeSkinKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; - } - - BOOL isDownLoadNoImageIn3G = [[NSUserDefaults standardUserDefaults] boolForKey:IsDownLoadNoImageIn3GKey]; - if (!isDownLoadNoImageIn3G) { - [[NSUserDefaults standardUserDefaults] setObject:@(NO) forKey:IsDownLoadNoImageIn3GKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; - } - - NSString *userName = [[NSUserDefaults standardUserDefaults] stringForKey:UserNameKey]; - if (userName==nil) { - [[NSUserDefaults standardUserDefaults] setObject:@"TTNews" forKey:UserNameKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; - } - - NSString *userSignature = [[NSUserDefaults standardUserDefaults] stringForKey:UserSignatureKey]; - if (userSignature==nil) { - [[NSUserDefaults standardUserDefaults] setObject:@"这个家伙很懒,什么也没有留下" forKey:UserSignatureKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; - } -} - -- (void)applicationWillResignActive:(UIApplication *)application { - // 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. - // 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. -} - -- (void)applicationDidEnterBackground:(UIApplication *)application { - // 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. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. -} - -- (void)applicationWillEnterForeground:(UIApplication *)application { - // 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. -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - // 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. -} - -- (void)applicationWillTerminate:(UIApplication *)application { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. -} - -@end diff --git a/TTNews/Classes/Other/Main/TTNavigationController.h b/TTNews/Classes/Other/Main/TTNavigationController.h deleted file mode 100644 index c668ae2..0000000 --- a/TTNews/Classes/Other/Main/TTNavigationController.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// TTNavigationController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/25. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TTNavigationController : UINavigationController - --(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; -@end diff --git a/TTNews/Classes/Other/Main/TTNavigationController.m b/TTNews/Classes/Other/Main/TTNavigationController.m deleted file mode 100644 index 7546878..0000000 --- a/TTNews/Classes/Other/Main/TTNavigationController.m +++ /dev/null @@ -1,67 +0,0 @@ -// -// TTNavigationController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/25. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTNavigationController.h" -#import "TTConst.h" -#import -#import -@interface TTNavigationController () - -@end - -@implementation TTNavigationController - -- (void)viewDidLoad { - [super viewDidLoad]; - [self.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor],NSForegroundColorAttributeName,nil]]; - -} - --(void)dealloc { -} --(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { - if (self.childViewControllers.count > 0) { // 如果push进来的不是第一个控制器 - UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; - [button setImage:[UIImage imageNamed:@"navigationbar_pic_back_icon"] forState:UIControlStateNormal]; - [button setImage:[UIImage imageNamed:@"navigationbar_back_icon"] forState:UIControlStateHighlighted]; - button.frame = CGRectMake(0, 0, 30, 30); - // 让按钮内部的所有内容左对齐 - button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; - // [button sizeToFit]; - // 让按钮的内容往左边偏移10 -// button.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - - [button addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside]; - - // 修改导航栏左边的item - viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button]; - - // 隐藏tabbar - viewController.hidesBottomBarWhenPushed = YES; - } - - // 这句super的push要放在后面, 让viewController可以覆盖上面设置的leftBarButtonItem - [super pushViewController:viewController animated:animated]; - - -} - -- (UIStatusBarStyle)preferredStatusBarStyle { - return UIStatusBarStyleLightContent; -} - -- (void)back -{ - [self popViewControllerAnimated:YES]; -} - --(void)didReceiveMemoryWarning { - [[SDImageCache sharedImageCache] clearDisk]; - -} -@end diff --git a/TTNews/Classes/Other/Main/TTTabBarController.h b/TTNews/Classes/Other/Main/TTTabBarController.h deleted file mode 100644 index 02c9c0c..0000000 --- a/TTNews/Classes/Other/Main/TTTabBarController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// TTTabBarController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/25. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TTTabBarController : UITabBarController - -@end diff --git a/TTNews/Classes/Other/Main/TTTabBarController.m b/TTNews/Classes/Other/Main/TTTabBarController.m deleted file mode 100644 index 1710c29..0000000 --- a/TTNews/Classes/Other/Main/TTTabBarController.m +++ /dev/null @@ -1,104 +0,0 @@ -// -// TTTabBarController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/25. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTTabBarController.h" -#import "TTNavigationController.h" -#import "NewsViewController.h" -#import "PictureViewController.h" -#import "VideoViewController.h" -#import "MeTableViewController.h" -#import "TTConst.h" -#import -#import - -@interface TTTabBarController () -{ - MeTableViewController *_MeController; -} -@property (nonatomic, assign) BOOL isShakeCanChangeSkin; - -@end - -@implementation TTTabBarController - -- (void)viewDidLoad { - [super viewDidLoad]; - - - NewsViewController *vc1 = [[NewsViewController alloc] init]; - [self addChildViewController:vc1 withImage:[UIImage imageNamed:@"tabbar_news"] selectedImage:[UIImage imageNamed:@"tabbar_news_hl"] withTittle:@"新闻"]; - - PictureViewController *vc2 = [[PictureViewController alloc] init]; - [self addChildViewController:vc2 withImage:[UIImage imageNamed:@"tabbar_picture"] selectedImage:[UIImage imageNamed:@"tabbar_picture_hl"] withTittle:@"图片"]; - - VideoViewController *vc3 = [[VideoViewController alloc] init]; - [self addChildViewController:vc3 withImage:[UIImage imageNamed:@"tabbar_video"] selectedImage:[UIImage imageNamed:@"tabbar_video_hl"] withTittle:@"视频"]; - - MeTableViewController *vc4 = [[MeTableViewController alloc] init]; - _MeController = vc4; - [self addChildViewController:vc4 withImage:[UIImage imageNamed:@"tabbar_setting"] selectedImage:[UIImage imageNamed:@"tabbar_setting_hl"] withTittle:@"我的"]; - vc4.delegate = self; - - [self setupBasic]; -} - --(void)setupBasic { - if ([self.dk_manager.themeVersion isEqualToString:DKThemeVersionNormal]) { - self.tabBar.barTintColor = [UIColor whiteColor]; - } else { - self.tabBar.barTintColor = [UIColor colorWithRed:34/255.0 green:34/255.0 blue:34/255.0 alpha:1.0]; - } - - [UIApplication sharedApplication].applicationSupportsShakeToEdit = YES; - [self becomeFirstResponder]; - self.isShakeCanChangeSkin = [[NSUserDefaults standardUserDefaults] boolForKey:IsShakeCanChangeSkinKey]; - - -} - --(void)dealloc { - -} - - --(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { - if (motion == UIEventSubtypeMotionShake) { - if (self.isShakeCanChangeSkin == NO) return; - if ([self.dk_manager.themeVersion isEqualToString:DKThemeVersionNormal]) {//将要切换至夜间模式 - self.dk_manager.themeVersion = DKThemeVersionNight; self.tabBar.barTintColor = [UIColor colorWithRed:34/255.0 green:34/255.0 blue:34/255.0 alpha:1.0]; - _MeController.changeSkinSwitch.on = YES; - } else { - self.dk_manager.themeVersion = DKThemeVersionNormal; self.tabBar.barTintColor = [UIColor whiteColor]; - _MeController.changeSkinSwitch.on = NO; - - } - } -} - -- (void)addChildViewController:(UIViewController *)controller withImage:(UIImage *)image selectedImage:(UIImage *)selectImage withTittle:(NSString *)tittle{ - TTNavigationController *nav = [[TTNavigationController alloc] initWithRootViewController:controller]; - - [nav.tabBarItem setImage:image]; - [nav.tabBarItem setSelectedImage:selectImage]; -// nav.tabBarItem.title = tittle; -// controller.navigationItem.title = tittle; - controller.title = tittle;//这句代码相当于上面两句代码 - [nav.tabBarItem setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]} forState:UIControlStateSelected]; - nav.tabBarItem.titlePositionAdjustment = UIOffsetMake(0, -3); - [self addChildViewController:nav]; -} - --(void)shakeCanChangeSkin:(BOOL)status { - self.isShakeCanChangeSkin = status; -} - --(void)didReceiveMemoryWarning { - [[SDImageCache sharedImageCache] clearDisk]; - -} -@end diff --git a/TTNews/Classes/Other/NetWork/Reachability.h b/TTNews/Classes/Other/NetWork/Reachability.h deleted file mode 100644 index 26e1c79..0000000 --- a/TTNews/Classes/Other/NetWork/Reachability.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright (C) 2015 Apple Inc. All Rights Reserved. - See LICENSE.txt for this sample’s licensing information - - Abstract: - Basic demonstration of how to use the SystemConfiguration Reachablity APIs. - */ - -#import -#import -#import - - -typedef enum : NSInteger { - NotReachable = 0, - ReachableViaWiFi, - ReachableViaWWAN -} NetworkStatus; - - -extern NSString *kReachabilityChangedNotification; - - -@interface Reachability : NSObject - -/*! - * Use to check the reachability of a given host name. - */ -+ (instancetype)reachabilityWithHostName:(NSString *)hostName; - -/*! - * Use to check the reachability of a given IP address. - */ -+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress; - -/*! - * Checks whether the default route is available. Should be used by applications that do not connect to a particular host. - */ -+ (instancetype)reachabilityForInternetConnection; - -/*! - * Checks whether a local WiFi connection is available. - */ -+ (instancetype)reachabilityForLocalWiFi; - -/*! - * Start listening for reachability notifications on the current run loop. - */ -- (BOOL)startNotifier; -- (void)stopNotifier; - -- (NetworkStatus)currentReachabilityStatus; - -/*! - * WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand. - */ -- (BOOL)connectionRequired; - -@end - - diff --git a/TTNews/Classes/Other/NetWork/Reachability.m b/TTNews/Classes/Other/NetWork/Reachability.m deleted file mode 100644 index 0c198f8..0000000 --- a/TTNews/Classes/Other/NetWork/Reachability.m +++ /dev/null @@ -1,278 +0,0 @@ -/* - Copyright (C) 2015 Apple Inc. All Rights Reserved. - See LICENSE.txt for this sample’s licensing information - - Abstract: - Basic demonstration of how to use the SystemConfiguration Reachablity APIs. - */ - -#import -#import -#import -#import - -#import - -#import "Reachability.h" - - -NSString *kReachabilityChangedNotification = @"kNetworkReachabilityChangedNotification"; - - -#pragma mark - Supporting functions - -#define kShouldPrintReachabilityFlags 1 - -static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment) -{ -#if kShouldPrintReachabilityFlags - -/* NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n", - (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', - (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', - - (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', - (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', - (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', - (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', - (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', - (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', - (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-', - comment - );*/ -#endif -} - - -static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) -{ -#pragma unused (target, flags) - NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); - NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback"); - - Reachability* noteObject = (__bridge Reachability *)info; - // Post a notification to notify the client that the network reachability changed. - [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject]; -} - - -#pragma mark - Reachability implementation - -@implementation Reachability -{ - BOOL _alwaysReturnLocalWiFiStatus; //default is NO - SCNetworkReachabilityRef _reachabilityRef; -} - -+ (instancetype)reachabilityWithHostName:(NSString *)hostName -{ - Reachability* returnValue = NULL; - SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); - if (reachability != NULL) - { - returnValue= [[self alloc] init]; - if (returnValue != NULL) - { - returnValue->_reachabilityRef = reachability; - returnValue->_alwaysReturnLocalWiFiStatus = NO; - } - else { - CFRelease(reachability); - } - } - return returnValue; -} - - -+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress -{ - SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress); - - Reachability* returnValue = NULL; - - if (reachability != NULL) - { - returnValue = [[self alloc] init]; - if (returnValue != NULL) - { - returnValue->_reachabilityRef = reachability; - returnValue->_alwaysReturnLocalWiFiStatus = NO; - } - else { - CFRelease(reachability); - } - } - return returnValue; -} - - - -+ (instancetype)reachabilityForInternetConnection -{ - struct sockaddr_in zeroAddress; - bzero(&zeroAddress, sizeof(zeroAddress)); - zeroAddress.sin_len = sizeof(zeroAddress); - zeroAddress.sin_family = AF_INET; - - return [self reachabilityWithAddress:&zeroAddress]; -} - - -+ (instancetype)reachabilityForLocalWiFi -{ - struct sockaddr_in localWifiAddress; - bzero(&localWifiAddress, sizeof(localWifiAddress)); - localWifiAddress.sin_len = sizeof(localWifiAddress); - localWifiAddress.sin_family = AF_INET; - - // IN_LINKLOCALNETNUM is defined in as 169.254.0.0. - localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); - - Reachability* returnValue = [self reachabilityWithAddress: &localWifiAddress]; - if (returnValue != NULL) - { - returnValue->_alwaysReturnLocalWiFiStatus = YES; - } - - return returnValue; -} - - -#pragma mark - Start and stop notifier - -- (BOOL)startNotifier -{ - BOOL returnValue = NO; - SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; - - if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context)) - { - if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) - { - returnValue = YES; - } - } - - return returnValue; -} - - -- (void)stopNotifier -{ - if (_reachabilityRef != NULL) - { - SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - } -} - - -- (void)dealloc -{ - [self stopNotifier]; - if (_reachabilityRef != NULL) - { - CFRelease(_reachabilityRef); - } -} - - -#pragma mark - Network Flag Handling - -- (NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags -{ - PrintReachabilityFlags(flags, "localWiFiStatusForFlags"); - NetworkStatus returnValue = NotReachable; - - if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) - { - returnValue = ReachableViaWiFi; - } - - return returnValue; -} - - -- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags -{ - PrintReachabilityFlags(flags, "networkStatusForFlags"); - if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) - { - // The target host is not reachable. - return NotReachable; - } - - NetworkStatus returnValue = NotReachable; - - if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) - { - /* - If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi... - */ - returnValue = ReachableViaWiFi; - } - - if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || - (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) - { - /* - ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs... - */ - - if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) - { - /* - ... and no [user] intervention is needed... - */ - returnValue = ReachableViaWiFi; - } - } - - if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) - { - /* - ... but WWAN connections are OK if the calling application is using the CFNetwork APIs. - */ - returnValue = ReachableViaWWAN; - } - - return returnValue; -} - - -- (BOOL)connectionRequired -{ - NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef"); - SCNetworkReachabilityFlags flags; - - if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) - { - return (flags & kSCNetworkReachabilityFlagsConnectionRequired); - } - - return NO; -} - - -- (NetworkStatus)currentReachabilityStatus -{ - NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef"); - NetworkStatus returnValue = NotReachable; - SCNetworkReachabilityFlags flags; - - if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) - { - if (_alwaysReturnLocalWiFiStatus) - { - returnValue = [self localWiFiStatusForFlags:flags]; - } - else - { - returnValue = [self networkStatusForFlags:flags]; - } - } - - return returnValue; -} - - -@end diff --git a/TTNews/Classes/Other/NetWork/TTJudgeNetworking.h b/TTNews/Classes/Other/NetWork/TTJudgeNetworking.h deleted file mode 100644 index 73527f3..0000000 --- a/TTNews/Classes/Other/NetWork/TTJudgeNetworking.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// TTJudgeNetworking.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - - -@interface TTJudgeNetworking : NSObject - -typedef NS_ENUM(NSUInteger, NetworkingType) { - NetworkingTypeNoReachable = 1, - NetworkingType3G = 2, - NetworkingTypeWiFi = 3, -}; - -+ (BOOL)judge; - -+ (NetworkingType)currentNetworkingType; -@end diff --git a/TTNews/Classes/Other/NetWork/TTJudgeNetworking.m b/TTNews/Classes/Other/NetWork/TTJudgeNetworking.m deleted file mode 100644 index f59bd53..0000000 --- a/TTNews/Classes/Other/NetWork/TTJudgeNetworking.m +++ /dev/null @@ -1,30 +0,0 @@ -// -// TTJudgeNetworking.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/10. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTJudgeNetworking.h" -#import "Reachability.h" - -@implementation TTJudgeNetworking - -+ (BOOL)judge { - if ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] == NotReachable) { - return NO; - } - return YES; -} - -+(NetworkingType)currentNetworkingType { - Reachability *reachablility = [Reachability reachabilityWithHostName:@"www.baidu.com"]; - if ([reachablility currentReachabilityStatus] == ReachableViaWiFi) { - return NetworkingTypeWiFi; - } else if ([reachablility currentReachabilityStatus] == ReachableViaWWAN) { - return NetworkingType3G; - } - return NetworkingTypeNoReachable; -} -@end diff --git a/TTNews/Classes/Picture/Controller/PictureCommentViewController.h b/TTNews/Classes/Picture/Controller/PictureCommentViewController.h deleted file mode 100644 index e5374fa..0000000 --- a/TTNews/Classes/Picture/Controller/PictureCommentViewController.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// PictureCommentViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/25. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@class TTPicture; - -@interface PictureCommentViewController : UIViewController -/** 帖子模型 */ -@property (nonatomic, strong) TTPicture *picture; - -@end diff --git a/TTNews/Classes/Picture/Controller/PictureCommentViewController.m b/TTNews/Classes/Picture/Controller/PictureCommentViewController.m deleted file mode 100644 index 521afd2..0000000 --- a/TTNews/Classes/Picture/Controller/PictureCommentViewController.m +++ /dev/null @@ -1,360 +0,0 @@ -// -// PictureCommentViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/3/25. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// -#import "PictureCommentViewController.h" -#import "pictureTableViewCell.h" -#import "TTpicture.h" -#import -#import -#import "TTpictureComment.h" -#import -#import "PictureCommentCell.h" -#import "UIBarButtonItem+Extension.h" -#import "TTConst.h" -#import "UIView+Extension.h" -#import -#import "TTDataTool.h" -#import "TTNetworkManager.h" - -static NSString * const PictureCommentCellID = @"PictureCommentCell"; - - - -@interface PictureCommentViewController () -/** 工具条底部间距 */ -@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomSapce; -@property (weak, nonatomic) IBOutlet UITableView *tableView; -@property (weak, nonatomic) IBOutlet UIView *bottomContianerView; -@property (weak, nonatomic) IBOutlet UITextField *commentTextField; - -/** 最热评论 */ -@property (nonatomic, strong) NSArray *hotComments; -/** 最新评论 */ -@property (nonatomic, strong) NSMutableArray *latestComments; - -/** 保存帖子的top_cmt */ -@property (nonatomic, strong) TTPictureComment *saved_top_cmt; - -/** 保存当前的页码 */ -@property (nonatomic, assign) NSInteger page; - - -@property (nonatomic, copy) NSString *currentSkinModel; -@property (nonatomic, weak) PictureTableViewCell *headerPictureCell; - -@end - -@implementation PictureCommentViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - - [self setupBasic]; - - [self setupHeader]; - - [self setupRefresh]; -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - if (self.saved_top_cmt) { - self.picture.top_cmt = self.saved_top_cmt; - [self.picture setValue:@0 forKeyPath:@"cellHeight"]; - } - [[NSNotificationCenter defaultCenter] removeObserver:self]; - -} - -#pragma mark 基本设置 -- (void)setupBasic -{ - self.tableView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x000000, 0xfafafa); - self.bottomContianerView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x000000, 0xfafafa); - - self.title = @"评论"; - // 内边距s - self.automaticallyAdjustsScrollViewInsets = NO; - self.tableView.contentInset = UIEdgeInsetsMake(CGRectGetMaxY(self.navigationController.navigationBar.frame) + 10, 0, 0, 0); - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; - - // cell的高度设置 - self.tableView.estimatedRowHeight = 44; - self.tableView.rowHeight = UITableViewAutomaticDimension; - - // 背景色 - - // 注册 - [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([PictureCommentCell class]) bundle:nil] forCellReuseIdentifier:PictureCommentCellID]; - - // 去掉分割线 - self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; -} - -#pragma mark 初始化刷新控件 -- (void)setupRefresh -{ - self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewComments)]; - self.tableView.mj_header.automaticallyChangeAlpha = YES; - [self.tableView.mj_header beginRefreshing]; - - self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreComments)]; - self.tableView.mj_footer.hidden = YES; -} - -#pragma mark 初始化TableView的headerView -- (void)setupHeader -{ - // 清空top_cmt - if (self.picture.top_cmt) { - self.saved_top_cmt = self.picture.top_cmt; - self.picture.top_cmt = nil; - self.picture.cellHeight = 0; - } - - // 创建header - UIView *header = [[UIView alloc] init]; - // 添加cell - PictureTableViewCell *cell = [PictureTableViewCell cell]; - self.headerPictureCell = cell; - cell.picture = self.picture; - cell.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, self.picture.cellHeight); - cell.contentView.frame = cell.bounds; - - // header的高度 - header.height = self.picture.cellHeight+ cellMargin; - [header addSubview:cell]; - - // 设置header - self.tableView.tableHeaderView = header; -} - -#pragma mark 加载更多评论 -- (void)loadMoreComments -{ - // 页码 - NSInteger page = self.page + 1; - - // 参数 - NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"a"] = @"dataList"; - params[@"c"] = @"comment"; - params[@"data_id"] = self.picture.ID; - params[@"page"] = @(page); - TTPictureComment *cmt = [self.latestComments lastObject]; - params[@"lastcid"] = cmt.ID; - -// [TTDataTool PictureCommentsWithParameters:params success:^(NSDictionary *responseObject) { - [[TTNetworkManager shareManager] Get:@"http://api.budejie.com/api/api_open.php" Parameters:params Success:^(NSURLSessionDataTask *task, id responseObject) { - - // 最新评论 - NSArray *newComments = [TTPictureComment mj_objectArrayWithKeyValuesArray:responseObject[@"data"]]; - [self.latestComments addObjectsFromArray:newComments]; - - // 页码 - self.page = page; - - // 刷新数据 - [self.tableView reloadData]; - - // 控制footer的状态 - NSInteger total = [responseObject[@"total"] integerValue]; - if (self.latestComments.count >= total) { // 全部加载完毕 - self.tableView.mj_footer.hidden = YES; - } else { - // 结束刷新状态 - [self.tableView.mj_footer endRefreshing]; - } - } Failure:^(NSError *error) { - [self.tableView.mj_footer endRefreshing]; - }]; -} - -#pragma mark 加载最新评论 -- (void)loadNewComments -{ - // 参数 - NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"a"] = @"dataList"; - params[@"c"] = @"comment"; - params[@"data_id"] = self.picture.ID; - params[@"hot"] = @"1"; - - [[TTNetworkManager shareManager] Get:@"http://api.budejie.com/api/api_open.php" Parameters:params Success:^(NSURLSessionDataTask *task, id responseObject) { - - - // 最热评论 - self.hotComments = [TTPictureComment mj_objectArrayWithKeyValuesArray:responseObject[@"hot"]]; - // 最新评论 - self.latestComments = [TTPictureComment mj_objectArrayWithKeyValuesArray:responseObject[@"data"]]; - // 页码 - self.page = 1; - - // 结束刷新 - [self.tableView.mj_header endRefreshing]; - - // 刷新数据 - [self.tableView reloadData]; - NSLog(@"%@",[NSThread currentThread]); - // 控制footer的状态 - NSInteger total = [responseObject[@"total"] integerValue]; - if (self.latestComments.count >= total) { // 全部加载完毕 - self.tableView.mj_footer.hidden = YES; - } - } Failure:^(NSError *error) { - [self.tableView.mj_header endRefreshing]; - }]; -} - - -#pragma mark 接收到键盘frame将要变化的通知 -- (void)keyboardWillChangeFrame:(NSNotification *)note -{ - // 键盘显示\隐藏完毕的frame - CGRect frame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; - // 修改底部约束 - self.bottomSapce.constant = [UIScreen mainScreen].bounds.size.height - frame.origin.y; - // 动画时间 - CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; - // 动画 - [UIView animateWithDuration:duration animations:^{ - [self.view layoutIfNeeded]; - }]; -} - -#pragma mark 返回第section组的所有评论数组 -- (NSArray *)commentsInSection:(NSInteger)section -{ - if (section == 0) { - return self.hotComments.count ? self.hotComments : self.latestComments; - } - return self.latestComments; -} - -- (TTPictureComment *)commentInIndexPath:(NSIndexPath *)indexPath -{ - return [self commentsInSection:indexPath.section][indexPath.row]; -} - -#pragma mark - -#pragma mark -UITableViewDataSource 返回tableView的组数 -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - NSInteger hotCount = self.hotComments.count; - NSInteger latestCount = self.latestComments.count; - - if (hotCount) return 2; // 有"最热评论" + "最新评论" 2组 - if (latestCount) return 1; // 有"最新评论" 1 组 - return 0; -} - -#pragma mark -UITableViewDataSource 返回某一组对应的cell个数 -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - - NSInteger hotCount = self.hotComments.count; - NSInteger latestCount = self.latestComments.count; - - // 隐藏尾部控件 - tableView.mj_footer.hidden = (latestCount == 0); - - if (section == 0) { - return hotCount ? hotCount : latestCount; - } - - // 非第0组 - return latestCount; -} - -#pragma mark -UITableViewDataSource 返回tableView每一组section的HeaderView -- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section -{ - // 先从缓存池中找header - UILabel *sectionHeaderLabel = [[UILabel alloc] init]; - sectionHeaderLabel.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20); - - // 设置label的数据 - NSInteger hotCount = self.hotComments.count; - if (section == 0) { - sectionHeaderLabel.text = hotCount ? @" 最热评论" : @" 最新评论"; - } else { - sectionHeaderLabel.text = @" 最新评论"; - } - - sectionHeaderLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - return sectionHeaderLabel; - -} - -#pragma mark -UITableViewDataSource 返回indexPath对应的cell -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - - PictureCommentCell *cell = [tableView dequeueReusableCellWithIdentifier:PictureCommentCellID]; - - cell.comment = [self commentInIndexPath:indexPath]; - - return cell; -} - -#pragma mark -UIScrollViewDelegate scrollView将要开始滑动 -- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView -{ - [self.view endEditing:YES]; - [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES]; -} - -#pragma mark -UITableViewDelegate 点击了tableView的某一行cell -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - - UIMenuController *menu = [UIMenuController sharedMenuController]; - if (menu.isMenuVisible) { - [menu setMenuVisible:NO animated:YES]; - } else { - // 被点击的cell - PictureCommentCell *cell = (PictureCommentCell *)[tableView cellForRowAtIndexPath:indexPath]; - // 出现一个第一响应者 - [cell becomeFirstResponder]; - - // 显示MenuController - UIMenuItem *ding = [[UIMenuItem alloc] initWithTitle:@"顶" action:@selector(ding:)]; - UIMenuItem *replay = [[UIMenuItem alloc] initWithTitle:@"回复" action:@selector(replay:)]; - UIMenuItem *report = [[UIMenuItem alloc] initWithTitle:@"举报" action:@selector(report:)]; - menu.menuItems = @[ding, replay, report]; - CGRect rect = CGRectMake(0, cell.frame.size.height * 0.5, cell.frame.size.width, cell.frame.size.height * 0.5); - [menu setTargetRect:rect inView:cell]; - [menu setMenuVisible:YES animated:YES]; - } -} - -#pragma mark - MenuItem处理 -- (void)ding:(UIMenuController *)menu -{ - - NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; - NSLog(@"%s %@", __func__, [self commentInIndexPath:indexPath].content); -} - -- (void)replay:(UIMenuController *)menu -{ - NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; - NSLog(@"%s %@", __func__, [self commentInIndexPath:indexPath].content); -} - -- (void)report:(UIMenuController *)menu -{ - NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; - NSLog(@"%s %@", __func__, [self commentInIndexPath:indexPath].content); -} -@end diff --git a/TTNews/Classes/Picture/Controller/PictureCommentViewController.xib b/TTNews/Classes/Picture/Controller/PictureCommentViewController.xib deleted file mode 100644 index 805261f..0000000 --- a/TTNews/Classes/Picture/Controller/PictureCommentViewController.xib +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/Picture/Controller/PictureViewController.h b/TTNews/Classes/Picture/Controller/PictureViewController.h deleted file mode 100644 index 90f4593..0000000 --- a/TTNews/Classes/Picture/Controller/PictureViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// PictureViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/25. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface PictureViewController : UITableViewController - -@end diff --git a/TTNews/Classes/Picture/Controller/PictureViewController.m b/TTNews/Classes/Picture/Controller/PictureViewController.m deleted file mode 100644 index 7e774f1..0000000 --- a/TTNews/Classes/Picture/Controller/PictureViewController.m +++ /dev/null @@ -1,198 +0,0 @@ -// -// PictureViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/3/25. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "PictureViewController.h" -#import -#import -#import -#import "PictureTableViewCell.h" -#import "TTPicture.h" -#import "PictureCommentViewController.h" -#import "TTDataTool.h" -#import "TTPictureFetchDataParameter.h" -#import "TTConst.h" -#import "TTJudgeNetworking.h" -#import - -@interface PictureViewController () - -@property (nonatomic, strong) NSMutableArray *pictureArray; -@property (nonatomic, assign) NSInteger currentPage; -@property (nonatomic, strong) NSString *maxtime; -@property (nonatomic, strong) NSDictionary *parameters; -@property (nonatomic, copy) NSString *currentSkinModel; - -@end -static NSString * const PictureCell = @"PictureCell"; - -@implementation PictureViewController - -#pragma mark 懒加载 --(NSMutableArray *)pictureArray { - if (!_pictureArray) { - _pictureArray = [NSMutableArray array]; - } - return _pictureArray; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - [self setupBasic]; - [self setupTableView]; - [self setupMJRefreshHeader]; -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [[NSNotificationCenter defaultCenter] removeObserver:self]; -// self.navigationController.navigationBar.alpha = 1; - -} - - - -#pragma mark 基本设置 -- (void)setupBasic { - self.tableView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x000000, 0xfafafa); - - self.navigationController.navigationBar.dk_barTintColorPicker = DKColorPickerWithRGB(0xfa5054,0x444444,0xfa5054); - - - self.currentPage = 0; -} - -#pragma mark 初始化tableview -- (void)setupTableView { - self.automaticallyAdjustsScrollViewInsets = NO; - self.tableView.contentInset = UIEdgeInsetsMake(CGRectGetMaxY(self.navigationController.navigationBar.frame) + 10, 0, 0, 0); - self.tableView.scrollIndicatorInsets = self.tableView.contentInset; - self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; - [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([PictureTableViewCell class]) bundle:nil] forCellReuseIdentifier:PictureCell]; -} - -#pragma mark --初始化刷新控件 -- (void)setupMJRefreshHeader { - self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(LoadNewData)]; - self.tableView.mj_header.automaticallyChangeAlpha = YES; - [self.tableView.mj_header beginRefreshing]; - - self.tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(LoadMoreData)]; -} - -#pragma mark --下拉刷新数据 -- (void)LoadNewData { - TTPictureFetchDataParameter *params = [[TTPictureFetchDataParameter alloc] init]; - TTPicture *firstPicture= self.pictureArray.firstObject; - params.recentTime = firstPicture.created_at; - params.page = 0; - params.maxtime = nil; - [TTDataTool pictureWithParameters:params success:^(NSArray *array, NSString *maxtime){ - self.maxtime = maxtime; - self.pictureArray = [array mutableCopy]; - [self.tableView reloadData]; - [self.tableView.mj_header endRefreshing]; - } failure:^(NSError *error) { - NSLog(@"%@%@",self,error); - }]; - -} - -#pragma mark --上拉加载更多数据 -- (void)LoadMoreData { - TTPictureFetchDataParameter *params= [[TTPictureFetchDataParameter alloc] init]; - TTPicture *lastPicture = self.pictureArray.lastObject; - self.currentPage = self.currentPage+1; - params.page = self.currentPage; - params.remoteTime = lastPicture.created_at; - params.maxtime = self.maxtime; - [TTDataTool pictureWithParameters:params success:^(NSArray *array, NSString *maxtime){ - self.maxtime = maxtime; - [self.pictureArray addObjectsFromArray:array]; - [self.tableView reloadData]; - [self.tableView.mj_footer endRefreshing]; - } failure:^(NSError *error) { - self.currentPage = self.currentPage-1; - NSLog(@"%@%@",self,error); - }]; -} - -#pragma mark - Table view data source - -#pragma mark -UITableViewDataSource 返回tableView有多少组 -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 1; -} - -#pragma mark -UITableViewDataSource 返回tableView每一组有多少行 -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return self.pictureArray.count; -} - - -#pragma mark -UITableViewDataSource 返回indexPath对应的cell -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - PictureTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PictureCell]; - cell.picture = self.pictureArray[indexPath.row]; - cell.delegate = self; - return cell; -} - -#pragma mark -UITableViewDataSource 返回indexPath对应的cell的高度 -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - TTPicture *picture = self.pictureArray[indexPath.row]; - return picture.cellHeight; -} - -#pragma mark -UITableViewDelegate 点击了某个cell --(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - [self pushToPictureCommentViewControllerWithIndexPath:indexPath]; -} - -#pragma mark 点击某个Cell或点击评论按钮跳转到评论页面 --(void)pushToPictureCommentViewControllerWithIndexPath:(NSIndexPath *)indexPath { - PictureCommentViewController *viewController = [[PictureCommentViewController alloc] init]; - viewController.picture = self.pictureArray[indexPath.row]; - [self.navigationController pushViewController:viewController animated:YES]; -} - -#pragma mark PictureTableViewCell代理 点击更多按钮 --(void)clickMoreButton:(TTPicture *)picture { - UIAlertController *controller = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - [controller addAction:[UIAlertAction actionWithTitle:@"收藏" style:UIAlertActionStyleDefault handler:nil]]; - [controller addAction:[UIAlertAction actionWithTitle:@"举报" style:UIAlertActionStyleDefault handler:nil]]; - [controller addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; - [self presentViewController:controller animated:YES completion:nil]; -} - -#pragma mark PictureTableViewCell代理 点击评论按钮 --(void)clickCommentButton:(NSIndexPath *)indexPath { - [self pushToPictureCommentViewControllerWithIndexPath:indexPath]; -} - -#pragma mark --UIScrollViewDelegate scrollView滑动了 --(void)scrollViewDidScroll:(UIScrollView *)scrollView { -// if (self.tableView.contentOffset.y>0) { -// self.navigationController.navigationBar.alpha = 0; -// } else { -// CGFloat yValue = - self.tableView.contentOffset.y;//纵向的差距 -// CGFloat alphValue = yValue/self.tableView.contentInset.top; -// self.navigationController.navigationBar.alpha =alphValue; -// } -} - --(void)didReceiveMemoryWarning { - [[SDImageCache sharedImageCache] clearDisk]; - -} - -@end diff --git a/TTNews/Classes/Picture/Model/TTPicture.h b/TTNews/Classes/Picture/Model/TTPicture.h deleted file mode 100644 index c228eb7..0000000 --- a/TTNews/Classes/Picture/Model/TTPicture.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// TTPicture.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@class TTPictureComment; -@interface TTPicture : NSObject - -@property (nonatomic, copy) NSString *ID; -@property (nonatomic, copy) NSString *profile_image; -@property (nonatomic, copy) NSString *screen_name; -@property (nonatomic, copy) NSString *created_at; -@property (nonatomic, copy) NSString *text; -@property (nonatomic, copy) NSString *love; -@property (nonatomic, copy) NSString *cai; -@property (nonatomic, copy) NSString *repost; -@property (nonatomic, copy) NSString *comment; -@property (nonatomic, copy) NSString *maxtime; -@property (nonatomic, assign, getter=isSina_v) BOOL sina_v; -@property (nonatomic, assign) CGFloat width; -@property (nonatomic, assign) CGFloat height; -@property (nonatomic, copy) NSString *image0; -@property (nonatomic, copy) NSString *image1; -@property (nonatomic, copy) NSString *image2; -@property (nonatomic, copy) NSString *playcount; -@property (nonatomic, copy) NSString *videotime; -@property (nonatomic, copy) NSString *videouri; - -@property (nonatomic, strong) TTPictureComment *top_cmt; -@property (nonatomic, assign) CGFloat cellHeight; -@property (nonatomic, assign) CGRect pictureFrame; -@property (nonatomic, assign, getter=isBigPicture) BOOL bigPicture; - -@end diff --git a/TTNews/Classes/Picture/Model/TTPicture.m b/TTNews/Classes/Picture/Model/TTPicture.m deleted file mode 100644 index d0a0fe4..0000000 --- a/TTNews/Classes/Picture/Model/TTPicture.m +++ /dev/null @@ -1,74 +0,0 @@ -// -// TTPicture.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTPicture.h" -#import "TTPictureComment.h" -#import "TTPictureUser.h" -#import -#import "TTConst.h" - -@implementation TTPicture - -+(NSDictionary *)mj_replacedKeyFromPropertyName { - return @{@"ID":@"id", - @"top_cmt":@"top_cmt[0]" - }; -} - --(CGFloat)cellHeight { - if (!_cellHeight) { - - CGSize maxSize = CGSizeMake([UIScreen mainScreen].bounds.size.width - 2*cellMargin, MAXFLOAT); - CGFloat TextHeight = [self.text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15]} context:nil].size.height; - - _cellHeight = cellMargin+cellTextY + TextHeight; - - //videoImageview的高度 - CGFloat pictureX = 0; - CGFloat pictureY = cellTextY + TextHeight + cellMargin; - CGFloat pictureWidth = [UIScreen mainScreen].bounds.size.width; - - CGFloat pictureHeight = self.height * pictureWidth/self.width; - if (pictureHeight > [UIScreen mainScreen].bounds.size.height-64-40) { - self.bigPicture = YES; - pictureHeight = maxSize.width * 9/16; - } else { - self.bigPicture = NO; - } - self.pictureFrame = CGRectMake(pictureX, pictureY, pictureWidth, pictureHeight); - _cellHeight += pictureHeight + cellMargin; - - - //热评的高度 - TTPictureComment *cmt = self.top_cmt; - if (cmt) {//最热评论存在 - NSString *content = [NSString stringWithFormat:@"%@ : %@", cmt.user.username, cmt.content]; - CGFloat topCommentHeight = [content boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13 ]} context:nil].size.height; - - _cellHeight += 0.5*cellMargin + cellTopCommentTopLabelHeight+topCommentHeight + 0.5*cellMargin + cellBottomBarHeight; - } else { - _cellHeight += 0.5*cellMargin+ 0.5*cellMargin +cellBottomBarHeight; - } - - } - - return _cellHeight; -} - --(instancetype)initWithCoder:(NSCoder *)aDecoder { - if (self= [super init]) { - [self mj_decode:aDecoder]; - } - return self; -} - --(void)encodeWithCoder:(NSCoder *)aCoder { - [self mj_encode:aCoder]; -} - -@end diff --git a/TTNews/Classes/Picture/Model/TTPictureComment.h b/TTNews/Classes/Picture/Model/TTPictureComment.h deleted file mode 100644 index 4c1f181..0000000 --- a/TTNews/Classes/Picture/Model/TTPictureComment.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// TTPictureComment.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@class TTPictureUser; - -@interface TTPictureComment : NSObject - -@property (nonatomic, copy) NSString *ID; -@property (nonatomic, copy) NSString *voiceUrl; -/** 音频文件的时长 */ -@property (nonatomic, assign) NSInteger voicetime; -/** 评论的文字内容 */ -@property (nonatomic, copy) NSString *content; -/** 被点赞的数量 */ -@property (nonatomic, assign) NSInteger like_count; -/** 用户 */ -@property (nonatomic, strong) TTPictureUser *user; - -@end \ No newline at end of file diff --git a/TTNews/Classes/Picture/Model/TTPictureComment.m b/TTNews/Classes/Picture/Model/TTPictureComment.m deleted file mode 100644 index 7ca5be6..0000000 --- a/TTNews/Classes/Picture/Model/TTPictureComment.m +++ /dev/null @@ -1,26 +0,0 @@ -// -// TTPictureComment.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTPictureComment.h" -#import - -@implementation TTPictureComment -+(NSDictionary *)mj_replacedKeyFromPropertyName { - return @{@"ID":@"id"}; -} - --(instancetype)initWithCoder:(NSCoder *)aDecoder { - if (self = [super init]) { - [self mj_decode:aDecoder]; - } - return self; -} --(void)encodeWithCoder:(NSCoder *)aCoder { - [self mj_encode:aCoder]; -} -@end diff --git a/TTNews/Classes/Picture/Model/TTPictureUser.h b/TTNews/Classes/Picture/Model/TTPictureUser.h deleted file mode 100644 index 4a7396b..0000000 --- a/TTNews/Classes/Picture/Model/TTPictureUser.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// TTPictureUser.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TTPictureUser : NSObject -@property (nonatomic, copy) NSString *username; -@property (nonatomic, copy) NSString *profile_image; -@property (nonatomic, copy) NSString *sex; -@end diff --git a/TTNews/Classes/Picture/Model/TTPictureUser.m b/TTNews/Classes/Picture/Model/TTPictureUser.m deleted file mode 100644 index 181304a..0000000 --- a/TTNews/Classes/Picture/Model/TTPictureUser.m +++ /dev/null @@ -1,25 +0,0 @@ -// -// TTPictureUser.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTPictureUser.h" -#import - -@implementation TTPictureUser - --(instancetype)initWithCoder:(NSCoder *)aDecoder { - if (self=[super init]) { - [self mj_decode:aDecoder]; - } - return self; -} - --(void)encodeWithCoder:(NSCoder *)aCoder { - [self mj_encode:aCoder]; -} - -@end diff --git a/TTNews/Classes/Picture/View/PictureCommentCell.h b/TTNews/Classes/Picture/View/PictureCommentCell.h deleted file mode 100644 index dc260d4..0000000 --- a/TTNews/Classes/Picture/View/PictureCommentCell.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// VideoCommentCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -@class TTPictureComment; - -@interface PictureCommentCell : UITableViewCell -/** 评论 */ -@property (nonatomic, strong) TTPictureComment *comment; - -@end diff --git a/TTNews/Classes/Picture/View/PictureCommentCell.m b/TTNews/Classes/Picture/View/PictureCommentCell.m deleted file mode 100644 index 2178b6f..0000000 --- a/TTNews/Classes/Picture/View/PictureCommentCell.m +++ /dev/null @@ -1,100 +0,0 @@ -// -// VideoCommentCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "PictureCommentCell.h" -#import "TTPictureComment.h" -#import "TTPictureUser.h" -#import -#import -#import - -@interface PictureCommentCell() -@property (weak, nonatomic) IBOutlet UIImageView *profileImageView; -@property (weak, nonatomic) IBOutlet UIImageView *sexView; -@property (weak, nonatomic) IBOutlet UILabel *contentLabel; -@property (weak, nonatomic) IBOutlet UILabel *usernameLabel; -@property (weak, nonatomic) IBOutlet UILabel *likeCountLabel; -@property (weak, nonatomic) IBOutlet UIButton *voiceButton; -@end - -@implementation PictureCommentCell - -- (BOOL)canBecomeFirstResponder -{ - return YES; -} - -- (BOOL)canPerformAction:(SEL)action withSender:(id)sender -{ - return NO; -} - -- (void)awakeFromNib -{ - [super awakeFromNib]; - UIImageView *bgView = [[UIImageView alloc] init]; - bgView.image = [UIImage imageNamed:@"mainCellBackground"]; - self.backgroundView = bgView; - self.contentView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - self.contentLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.usernameLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.likeCountLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - - - [SDWebImageManager sharedManager].delegate =self; -// self.profileImageView.layer.cornerRadius = self.profileImageView.width * 0.5; -// self.profileImageView.layer.masksToBounds = YES; -} - -- (void)setComment:(TTPictureComment *)comment -{ - _comment = comment; - - [self.profileImageView sd_setImageWithURL:[NSURL URLWithString:comment.user.profile_image] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"] options:SDWebImageTransformAnimatedImage]; - - - - self.sexView.image = [comment.user.sex isEqualToString:@"m"] ? [UIImage imageNamed:@"Profile_manIcon"] : [UIImage imageNamed:@"Profile_womanIcon"]; - self.contentLabel.text = comment.content; - self.usernameLabel.text = comment.user.username; - self.likeCountLabel.text = [NSString stringWithFormat:@"%zd", comment.like_count]; - - if (comment.voiceUrl.length) { - self.voiceButton.hidden = NO; - [self.voiceButton setTitle:[NSString stringWithFormat:@"%zd''", comment.voicetime] forState:UIControlStateNormal]; - } else { - self.voiceButton.hidden = YES; - } -} - -- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL { - - // NO代表透明 - UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0); - - // 获得上下文 - CGContextRef context = UIGraphicsGetCurrentContext(); - - // 添加一个圆 - CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height); - CGContextAddEllipseInRect(context, rect); - - // 裁剪 - CGContextClip(context); - - // 将图片画上去 - [image drawInRect:rect]; - - UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - - return resultImage; -} - -@end diff --git a/TTNews/Classes/Picture/View/PictureCommentCell.xib b/TTNews/Classes/Picture/View/PictureCommentCell.xib deleted file mode 100644 index c84ecaf..0000000 --- a/TTNews/Classes/Picture/View/PictureCommentCell.xib +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/Picture/View/PictureTableViewCell.h b/TTNews/Classes/Picture/View/PictureTableViewCell.h deleted file mode 100644 index 699b726..0000000 --- a/TTNews/Classes/Picture/View/PictureTableViewCell.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// PictureTableViewCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -@class TTPicture; - -@protocol PictureTableViewCellDelegate -@optional - --(void)clickMoreButton:(TTPicture *)picture; --(void)clickCommentButton:(NSIndexPath *)indexPath; - -@end - -@interface PictureTableViewCell : UITableViewCell - -+(instancetype)cell; - -@property (nonatomic, strong) TTPicture *picture; -@property (nonatomic, weak) id delegate; -@property (nonatomic, strong) NSIndexPath *indexPath; -@end diff --git a/TTNews/Classes/Picture/View/PictureTableViewCell.m b/TTNews/Classes/Picture/View/PictureTableViewCell.m deleted file mode 100644 index e9e7248..0000000 --- a/TTNews/Classes/Picture/View/PictureTableViewCell.m +++ /dev/null @@ -1,226 +0,0 @@ -// -// PictureTableViewCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "PictureTableViewCell.h" -#import "TTPicture.h" -#import "TTPictureComment.h" -#import "TTPictureUser.h" -#import "ShowBigPictureViewController.h" -#import -#import -#import -#import -#import - -@interface PictureTableViewCell() -@property (weak, nonatomic) IBOutlet UIImageView *headerImageView; -@property (weak, nonatomic) IBOutlet UILabel *nameLabel; -@property (weak, nonatomic) IBOutlet UILabel *TimeLabel; -@property (weak, nonatomic) IBOutlet UIButton *loveButton; -@property (weak, nonatomic) IBOutlet UIButton *hatebutton; -@property (weak, nonatomic) IBOutlet UIButton *repostButton; -@property (weak, nonatomic) IBOutlet UIButton *commentButton; -@property (weak, nonatomic) IBOutlet UILabel *contentLabel; -@property (weak, nonatomic) IBOutlet UIImageView *vipImageView; -@property (weak, nonatomic) IBOutlet UIImageView *pictureImageView; -@property (weak, nonatomic) IBOutlet UIButton *seeBigPictureButton; -@property (weak, nonatomic) IBOutlet UIImageView *gifImageview; -@property (weak, nonatomic) IBOutlet UILabel *topCommentTopLabel; -@property (weak, nonatomic) IBOutlet UILabel *topCommentLabel; -@property (weak, nonatomic) IBOutlet DALabeledCircularProgressView *progressView; - -@property (weak, nonatomic) IBOutlet UIView *separatorLine1; -@property (weak, nonatomic) IBOutlet UIView *separatorLine2; -@property (weak, nonatomic) IBOutlet UIView *separatorLine3; -@property (weak, nonatomic) IBOutlet UIView *separatorLine4; - -@end - -@implementation PictureTableViewCell - -+(instancetype)cell { - return [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] firstObject]; -} - -- (id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - } - return self; -} - -- (void)awakeFromNib { - [super awakeFromNib]; - - self.progressView.roundedCorners = 2; - self.progressView.progressLabel.textColor = [UIColor whiteColor]; - UIImageView *imageView = [[UIImageView alloc] init]; - imageView.image = [UIImage imageNamed:@"mainCellBackground"]; - self.backgroundView = imageView; - - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - self.contentView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - self.nameLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.contentLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.TimeLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.topCommentTopLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.topCommentLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - - self.separatorLine1.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); - self.separatorLine2.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); - self.separatorLine3.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); - self.separatorLine4.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); - [SDWebImageManager sharedManager].delegate =self; - -} - -- (void)setFrame:(CGRect)frame { - static CGFloat margin = 10; - frame.size.width = [UIScreen mainScreen].bounds.size.width; - frame.size.height = self.picture.cellHeight - margin; - [super setFrame:frame]; -} - --(void)setPicture:(TTPicture *)picture { - _picture = picture; - [self.headerImageView sd_setImageWithURL:[NSURL URLWithString:picture.profile_image] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"] options:SDWebImageTransformAnimatedImage]; - - self.nameLabel.text = picture.screen_name; - self.TimeLabel.text = picture.created_at; - self.contentLabel.text = picture.text; - self.vipImageView.hidden = !picture.isSina_v; - - NSString *extension = picture.image1.pathExtension; - [self.pictureImageView sd_setImageWithURL: - [NSURL URLWithString:picture.image1] placeholderImage:[UIImage imageNamed:@"allplaceholderImage"] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) { - self.progressView.hidden = NO; - CGFloat progress = 1.0*receivedSize/expectedSize; - NSString *text = [NSString stringWithFormat:@"%.0f%%", 100*progress]; - self.progressView.progressLabel.text = [text stringByReplacingOccurrencesOfString:@"-" withString:@""]; - [self.progressView setProgress:progress animated:YES]; - - } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - self.progressView.hidden = YES; - if (picture.isBigPicture) { - UIGraphicsBeginImageContextWithOptions(picture.pictureFrame.size, YES, 0.0); - CGFloat width = picture.pictureFrame.size.width; - CGFloat height = width * image.size.height / image.size.width; - [image drawInRect:CGRectMake(0, 0, width, height)]; - self.pictureImageView.image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - } - }]; - - if (![extension.lowercaseString isEqualToString:@"gif"]) { - self.gifImageview.hidden = YES; - } - - if (picture.isBigPicture) {//大图 - self.seeBigPictureButton.hidden = NO; - self.pictureImageView.contentMode = UIViewContentModeScaleAspectFill; - - } else {//非大图 - self.seeBigPictureButton.hidden = YES; - self.pictureImageView.contentMode = UIViewContentModeScaleToFill; - } - - [self setupButton:self.loveButton WithTittle:picture.love]; - [self setupButton:self.hatebutton WithTittle:picture.cai]; - [self setupButton:self.repostButton WithTittle:picture.repost]; - [self setupButton:self.commentButton WithTittle:picture.comment]; - - - TTPictureComment *comment = picture.top_cmt; - if(comment) { - self.topCommentLabel.text = [NSString stringWithFormat:@"%@ : %@",comment.user.username, comment.content]; - self.topCommentTopLabel.text = @"最热评论"; - } else { - self.topCommentLabel.text = @""; - self.topCommentTopLabel.text = @""; - - } - - -} - -- (void)setupButton:(UIButton *)button WithTittle:(NSString *)tittle { - double number = tittle.doubleValue; - if (number > 10000) { - [button setTitle:[NSString stringWithFormat:@"%.1f万",number/10000] forState:UIControlStateNormal]; - return; - } - [button setTitle:tittle forState:UIControlStateNormal]; - -} - -- (void)setSelected:(BOOL)selected animated:(BOOL)animated { - [super setSelected:selected animated:animated]; -} - -- (IBAction)more:(id)sender { - - if ([self.delegate respondsToSelector:@selector(clickMoreButton:)]) { - [self.delegate clickMoreButton:self.picture]; - } - -} - -- (IBAction)seeBigPicture:(id)sender { - - ShowBigPictureViewController *viewController = [[ShowBigPictureViewController alloc] init]; - viewController.picture = self.picture; - [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:viewController animated:YES completion:nil]; -} - -- (IBAction)love:(id)sender { - UIButton *button = (UIButton *)sender; - button.selected = !button.selected; -} - -- (IBAction)hate:(id)sender { - UIButton *button = (UIButton *)sender; - button.selected = !button.selected; -} - -- (IBAction)repost:(id)sender { - -} - -- (IBAction)comment:(id)sender { - if ([self.delegate respondsToSelector:@selector(clickCommentButton:)]) { - [self.delegate clickCommentButton:self.indexPath]; - } -} - -- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL { - - // NO代表透明 - UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0); - - // 获得上下文 - CGContextRef context = UIGraphicsGetCurrentContext(); - - // 添加一个圆 - CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height); - CGContextAddEllipseInRect(context, rect); - - // 裁剪 - CGContextClip(context); - - // 将图片画上去 - [image drawInRect:rect]; - - UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - - return resultImage; -} - - -@end diff --git a/TTNews/Classes/Picture/View/PictureTableViewCell.xib b/TTNews/Classes/Picture/View/PictureTableViewCell.xib deleted file mode 100644 index 0872122..0000000 --- a/TTNews/Classes/Picture/View/PictureTableViewCell.xib +++ /dev/null @@ -1,389 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/Picture/View/ShowBigPictureViewController.h b/TTNews/Classes/Picture/View/ShowBigPictureViewController.h deleted file mode 100644 index 97efc53..0000000 --- a/TTNews/Classes/Picture/View/ShowBigPictureViewController.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// ShowBigPictureViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -#import "TTPicture.h" -@interface ShowBigPictureViewController : UIViewController - -@property (nonatomic, strong) TTPicture *picture; -@end diff --git a/TTNews/Classes/Picture/View/ShowBigPictureViewController.m b/TTNews/Classes/Picture/View/ShowBigPictureViewController.m deleted file mode 100644 index 2e98f79..0000000 --- a/TTNews/Classes/Picture/View/ShowBigPictureViewController.m +++ /dev/null @@ -1,85 +0,0 @@ -// -// ShowBigPictureViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "ShowBigPictureViewController.h" -#import -#import -#import - -@interface ShowBigPictureViewController () -@property (weak, nonatomic) IBOutlet UIScrollView *scrollView; -@property (weak, nonatomic) IBOutlet DALabeledCircularProgressView *progressView; -@property (weak, nonatomic) UIImageView *imageView; -@end - -@implementation ShowBigPictureViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - CGFloat kDeviceWidth =[UIScreen mainScreen].bounds.size.width; - CGFloat kDeviceHeight =[UIScreen mainScreen].bounds.size.height; - - CGFloat imageWidth = kDeviceWidth; - CGFloat imageHeight = imageWidth * self.picture.height / self.picture.width; - - UIImageView *imageView = [[UIImageView alloc] init]; - UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(back:)]; - [imageView addGestureRecognizer:gesture]; - [self.scrollView addSubview:imageView]; - - self.imageView = imageView; - if (imageHeight <= kDeviceHeight) { - imageView.frame = CGRectMake(0, (kDeviceHeight-imageHeight)*0.5, imageWidth, imageHeight); - } else { - imageView.frame = CGRectMake(0, 0, imageWidth, imageHeight); - self.scrollView.contentSize = CGSizeMake(0, imageHeight); - } - - [self.imageView sd_setImageWithURL:[NSURL URLWithString:self.picture.image1] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) { - - self.progressView.hidden = NO; - CGFloat progress = 1.0*receivedSize/expectedSize; - NSString *text = [NSString stringWithFormat:@"%.0f%%", 100*progress]; - self.progressView.progressLabel.text = [text stringByReplacingOccurrencesOfString:@"-" withString:@""]; - [self.progressView setProgress:progress animated:YES]; - - } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { - self.progressView.hidden = YES; - }]; -} - -- (UIStatusBarStyle)preferredStatusBarStyle { - return UIStatusBarStyleLightContent; -} - -- (IBAction)savePicture:(id)sender { - if (!self.imageView.image) {//如果图片不存在 - [SVProgressHUD showErrorWithStatus:@"请在图片加载完毕后再保存图片"]; - } else { - UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); - } -} - -- (IBAction)repostPicture:(id)sender { - -} - -- (IBAction)back:(id)sender { - [self dismissViewControllerAnimated:YES completion:nil]; -} - -- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { - if (error) { - [SVProgressHUD showErrorWithStatus:[NSString stringWithFormat:@"保存失败\n%@",error]]; - } else { - [SVProgressHUD showErrorWithStatus:@"保存成功"]; - - } - -} -@end diff --git a/TTNews/Classes/Picture/View/ShowBigPictureViewController.xib b/TTNews/Classes/Picture/View/ShowBigPictureViewController.xib deleted file mode 100644 index c21b266..0000000 --- a/TTNews/Classes/Picture/View/ShowBigPictureViewController.xib +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/Video/Controller/VideoCommentViewController.h b/TTNews/Classes/Video/Controller/VideoCommentViewController.h deleted file mode 100644 index 8979133..0000000 --- a/TTNews/Classes/Video/Controller/VideoCommentViewController.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// VideoCommentViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// -#import - -@class TTVideo; -@class VideoTableViewCell; -@interface VideoCommentViewController : UIViewController -/** 帖子模型 */ -@property (nonatomic, strong) TTVideo *video; - -@end diff --git a/TTNews/Classes/Video/Controller/VideoCommentViewController.m b/TTNews/Classes/Video/Controller/VideoCommentViewController.m deleted file mode 100644 index 899016b..0000000 --- a/TTNews/Classes/Video/Controller/VideoCommentViewController.m +++ /dev/null @@ -1,420 +0,0 @@ -// -// VideoCommentViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "VideoCommentViewController.h" -#import "VideoTableViewCell.h" -#import "TTVideo.h" -#import -#import -#import "TTVideoComment.h" -#import -#import "VideoCommentCell.h" -#import "UIBarButtonItem+Extension.h" -#import "TTConst.h" -#import "UIView+Extension.h" -#import "FullViewController.h" -#import -#import "TTNetworkManager.h" - -static NSString * const VideoCommentCellID = @"VideoCommentCell"; - -@interface VideoCommentViewController () -/** 工具条底部间距 */ -@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomSapce; -@property (weak, nonatomic) IBOutlet UITableView *tableView; - -/** 最热评论 */ -@property (nonatomic, strong) NSArray *hotComments; -/** 最新评论 */ -@property (nonatomic, strong) NSMutableArray *latestComments; - -/** 保存帖子的top_cmt */ -@property (nonatomic, strong) TTVideoComment *saved_top_cmt; - -/** 保存当前的页码 */ -@property (nonatomic, assign) NSInteger page; - -/** 当前皮肤模式*/ -@property (nonatomic, copy) NSString *currentSkinModel; - -@property(nonatomic, weak) VideoTableViewCell *headerVideoCell; - -@property (weak, nonatomic) IBOutlet UIView *bottomContianerView; -@property (weak, nonatomic) IBOutlet UITextField *commentTextField; - -@property (nonatomic, assign) NSInteger total; - - -@property (nonatomic, strong) FullViewController *fullVc; -@property (nonatomic, weak) VideoPlayView *playView; -@property (nonatomic, assign) BOOL isFullScreenPlaying; - -@end - -@implementation VideoCommentViewController - -#pragma mark 懒加载 -- (void)viewDidLoad { - [super viewDidLoad]; - [self setupBasic]; - [self setupHeader]; - [self setupRefresh]; -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - if (self.isFullScreenPlaying == NO) {//将要呈现的画面不是全屏播放页面 - [self.playView resetPlayView]; - } - if (self.saved_top_cmt) { - self.video.top_cmt = self.saved_top_cmt; - [self.video setValue:@0 forKeyPath:@"cellHeight"]; - } - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - - -#pragma mark 基本设置 -- (void)setupBasic -{ - self.tableView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x000000, 0xfafafa); - self.bottomContianerView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x000000, 0xfafafa); - - self.title = @"评论"; - self.page = 1; - // cell的高度设置 - self.tableView.estimatedRowHeight = 44; - self.tableView.rowHeight = UITableViewAutomaticDimension; - - // 背景色 - - // 注册 - [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([VideoCommentCell class]) bundle:nil] forCellReuseIdentifier:VideoCommentCellID]; - - // 去掉分割线 - self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; - - // 内边距s - CGFloat top = CGRectGetMaxY(self.navigationController.navigationBar.frame); - self.tableView.contentInset = UIEdgeInsetsMake(top, 0, cellMargin, 0); - self.automaticallyAdjustsScrollViewInsets = NO; -} - - -#pragma mark 初始化刷新控件 -- (void)setupRefresh -{ - self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewComments)]; - self.tableView.mj_header.automaticallyChangeAlpha = YES; - [self.tableView.mj_header beginRefreshing]; - self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreComments)]; - self.tableView.mj_footer.hidden = YES; -} - -#pragma mark 初始化TableView的headerView -- (void)setupHeader -{ - // 清空top_cmt - if (self.video.top_cmt) { - self.saved_top_cmt = self.video.top_cmt; - self.video.top_cmt = nil; - self.video.cellHeight = 0; - } - - // 创建header - UIView *header = [[UIView alloc] init]; - // 添加cell - VideoTableViewCell *cell = [VideoTableViewCell cell]; - self.headerVideoCell = cell; - cell.delegate = self; - cell.video = self.video; - cell.frame = CGRectMake(0, cellMargin, [UIScreen mainScreen].bounds.size.width, self.video.cellHeight); - cell.contentView.frame = cell.bounds; - - // header的高度 - header.height = self.video.cellHeight + cellMargin; - [header addSubview:cell]; - - // 设置header - self.tableView.tableHeaderView = header; -} - -#pragma mark 加载最新评论 -- (void)loadNewComments -{ - // 参数 - NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"a"] = @"dataList"; - params[@"c"] = @"comment"; - params[@"data_id"] = self.video.ID; - params[@"hot"] = @"1"; - -// [TTDataTool VideoCommentsWithParameters:params success:^(NSDictionary *responseObject) { - [[TTNetworkManager shareManager] Get:@"http://api.budejie.com/api/api_open.php" Parameters:params Success:^(NSURLSessionDataTask *task, id responseObject) { - - // 最热评论 - self.hotComments = [TTVideoComment mj_objectArrayWithKeyValuesArray:responseObject[@"hot"]]; - // 最新评论 - self.latestComments = [TTVideoComment mj_objectArrayWithKeyValuesArray:responseObject[@"data"]]; - // 页码 - self.page = 1; - - // 刷新数据 - [self.tableView reloadData]; - // 结束刷新 - [self.tableView.mj_header endRefreshing]; - - // 控制footer的状态 - NSInteger total = [responseObject[@"total"] integerValue]; - if (self.latestComments.count >= total) { // 全部加载完毕 - self.tableView.mj_footer.hidden = YES; - } - } Failure:^(NSError *error) { - [self.tableView.mj_header endRefreshing]; - }]; -} - -#pragma mark 加载更多评论 -- (void)loadMoreComments -{ - - // 页码 - NSInteger page = self.page + 1; - - // 参数 - NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"a"] = @"dataList"; - params[@"c"] = @"comment"; - params[@"data_id"] = self.video.ID; - params[@"page"] = @(page); - TTVideoComment *cmt = [self.latestComments lastObject]; - params[@"lastcid"] = cmt.ID; - -// [TTDataTool VideoCommentsWithParameters:params success:^(NSDictionary *responseObject) { - // 没有数据 - [[TTNetworkManager shareManager] Get:@"http://api.budejie.com/api/api_open.php" Parameters:params Success:^(NSURLSessionDataTask *task, id responseObject) { - - // 最新评论 - NSArray *newComments = [TTVideoComment mj_objectArrayWithKeyValuesArray:responseObject[@"data"]]; - [self.latestComments addObjectsFromArray:newComments]; - - // 页码 - self.page = page; - - // 刷新数据 - [self.tableView reloadData]; - - // 控制footer的状态 - NSInteger total = [responseObject[@"total"] integerValue]; - if (self.latestComments.count >= total) { // 全部加载完毕 - self.tableView.mj_footer.hidden = YES; - } else { - // 结束刷新状态 - [self.tableView.mj_footer endRefreshing]; - } - } Failure:^(NSError *error) { - [self.tableView.mj_footer endRefreshing]; - }]; -} - -#pragma mark 接收到键盘frame将要变化的通知 -- (void)keyboardWillChangeFrame:(NSNotification *)note -{ - // 键盘显示\隐藏完毕的frame - CGRect frame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; - // 修改底部约束 - self.bottomSapce.constant = [UIScreen mainScreen].bounds.size.height - frame.origin.y; - // 动画时间 - CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; - // 动画 - [UIView animateWithDuration:duration animations:^{ - [self.view layoutIfNeeded]; - }]; -} - -#pragma mark 返回第section组的所有评论数组 -- (NSArray *)commentsInSection:(NSInteger)section -{ - if (section == 0) { - return self.hotComments.count ? self.hotComments : self.latestComments; - } - return self.latestComments; -} - -#pragma mark 返回indexPath对应的模型数据 -- (TTVideoComment *)commentInIndexPath:(NSIndexPath *)indexPath -{ - return [self commentsInSection:indexPath.section][indexPath.row]; -} - -#pragma mark - -#pragma mark -UITableViewDataSource 返回tableView有多少组 -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - NSInteger hotCount = self.hotComments.count; - NSInteger latestCount = self.latestComments.count; - - if (hotCount) return 2; // 有"最热评论" + "最新评论" 2组 - if (latestCount) return 1; // 有"最新评论" 1 组 - return 0; -} - -#pragma mark -UITableViewDataSource 返回tableView某一组对应的cell个数 -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - NSInteger hotCount = self.hotComments.count; - NSInteger latestCount = self.latestComments.count; - - // 隐藏尾部控件 - tableView.mj_footer.hidden = (latestCount == 0); - - if (section == 0) { - return hotCount ? hotCount : latestCount; - } - // 非第0组 - return latestCount; -} - -#pragma mark -UITableViewDataSource 返回tableView每一组section的HeaderView -- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section -{ - // 先从缓存池中找header - UILabel *sectionHeaderLabel = [[UILabel alloc] init]; - sectionHeaderLabel.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20); - - // 设置label的数据 - NSInteger hotCount = self.hotComments.count; - if (section == 0) { - sectionHeaderLabel.text = hotCount ? @" 最热评论" : @" 最新评论"; - } else { - sectionHeaderLabel.text = @" 最新评论"; - } - - - sectionHeaderLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - - return sectionHeaderLabel; -} - -#pragma mark -UITableViewDataSource 返回indexPath对应的cell的高度 -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - - VideoCommentCell *cell = [tableView dequeueReusableCellWithIdentifier:VideoCommentCellID]; - cell.comment = [self commentInIndexPath:indexPath]; - - return cell; -} - - -#pragma mark -UITableViewDelegate 点击了TableView的某一行cell -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - UIMenuController *menu = [UIMenuController sharedMenuController]; - if (menu.isMenuVisible) { - [menu setMenuVisible:NO animated:YES]; - } else { - // 被点击的cell - VideoCommentCell *cell = (VideoCommentCell *)[tableView cellForRowAtIndexPath:indexPath]; - // 出现一个第一响应者 - [cell becomeFirstResponder]; - - // 显示MenuController - UIMenuItem *ding = [[UIMenuItem alloc] initWithTitle:@"顶" action:@selector(ding:)]; - UIMenuItem *replay = [[UIMenuItem alloc] initWithTitle:@"回复" action:@selector(replay:)]; - UIMenuItem *report = [[UIMenuItem alloc] initWithTitle:@"举报" action:@selector(report:)]; - menu.menuItems = @[ding, replay, report]; - CGRect rect = CGRectMake(0, cell.frame.size.height * 0.5, cell.frame.size.width, cell.frame.size.height * 0.5); - [menu setTargetRect:rect inView:cell]; - [menu setMenuVisible:YES animated:YES]; - } -} - -#pragma mark -UIScrollViewDelegate scrollView将要开始滑动 -- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView -{ - [self.view endEditing:YES]; - [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES]; -} - -#pragma mark - MenuItem处理 -- (void)ding:(UIMenuController *)menu -{ - - NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; - NSLog(@"%s %@", __func__, [self commentInIndexPath:indexPath].content); -} - -- (void)replay:(UIMenuController *)menu -{ - NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; - NSLog(@"%s %@", __func__, [self commentInIndexPath:indexPath].content); -} - -- (void)report:(UIMenuController *)menu -{ - NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; - NSLog(@"%s %@", __func__, [self commentInIndexPath:indexPath].content); -} - -#pragma mark VideoTableViewCell的代理方法 --(void)clickVideoButton:(NSIndexPath *)indexPath { - [self.playView resetPlayView]; - VideoPlayView *playView = [VideoPlayView videoPlayView]; - playView.frame = self.video.videoFrame; - [self.headerVideoCell addSubview:playView]; - self.headerVideoCell.playView = playView; - self.playView = playView; - self.playView.delegate = self; - AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:self.video.videouri]]; - self.playView.playerItem = item; -} - -#pragma mark 视频播放时窗口模式与全屏模式切换 -- (void)videoplayViewSwitchOrientation:(BOOL)isFull -{ - if (isFull) { - self.isFullScreenPlaying = YES; - [self presentViewController:self.fullVc animated:YES completion:^{ - self.playView.frame = self.fullVc.view.bounds; - [self.fullVc.view addSubview:self.playView]; - }]; - } else { - [self.fullVc dismissViewControllerAnimated:YES completion:^{ - self.playView.frame = self.headerVideoCell.video.videoFrame; - [self.headerVideoCell addSubview:self.playView]; - self.isFullScreenPlaying = NO; - - }]; - - } -} - -#pragma mark --UIScrollViewDelegate--scrollView滑动了 --(void)scrollViewDidScroll:(UIScrollView *)scrollView { - if (self.playView.superview && self.isFullScreenPlaying == NO) {//点全屏和退出的 - [self.playView resetPlayView]; - } -} - - -#pragma mark - 懒加载代码 -- (FullViewController *)fullVc -{ - if (_fullVc == nil) { - self.fullVc = [[FullViewController alloc] init]; - } - return _fullVc; -} - -@end diff --git a/TTNews/Classes/Video/Controller/VideoCommentViewController.xib b/TTNews/Classes/Video/Controller/VideoCommentViewController.xib deleted file mode 100644 index 3fc2714..0000000 --- a/TTNews/Classes/Video/Controller/VideoCommentViewController.xib +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/Video/Controller/VideoViewController.h b/TTNews/Classes/Video/Controller/VideoViewController.h deleted file mode 100644 index a2a87ed..0000000 --- a/TTNews/Classes/Video/Controller/VideoViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// VideoViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface VideoViewController : UITableViewController - -@end diff --git a/TTNews/Classes/Video/Controller/VideoViewController.m b/TTNews/Classes/Video/Controller/VideoViewController.m deleted file mode 100644 index dcb2bae..0000000 --- a/TTNews/Classes/Video/Controller/VideoViewController.m +++ /dev/null @@ -1,262 +0,0 @@ -// -// VideoViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "VideoViewController.h" -#import -#import -#import -#import "TTVideo.h" -#import "TTVideoFetchDataParameter.h" -#import "VideoTableViewCell.h" -#import "VideoPlayView.h" -#import "FullViewController.h" -#import "VideoCommentViewController.h" -#import "TTDataTool.h" -#import "TTConst.h" -#import "TTJudgeNetworking.h" -#import - -@interface VideoViewController () - -@property (nonatomic, strong) NSMutableArray *videoArray; -@property (nonatomic, assign) NSInteger currentPage; -@property (nonatomic, strong) NSString *maxtime; -@property (nonatomic, strong) NSDictionary *parameters; -@property (nonatomic, strong) FullViewController *fullVc; -@property (nonatomic, weak) VideoPlayView *playView; -@property (nonatomic, weak) VideoTableViewCell *currentSelectedCell; -@property (nonatomic, copy) NSString *currentSkinModel; -@property (nonatomic, assign) BOOL isFullScreenPlaying; - -@end - -static NSString * const VideoCell = @"VideoCell"; - -@implementation VideoViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - [self setupBasic]; - [self setupTableView]; - [self setupMJRefreshHeader]; -} - --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; -} - --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - if (self.isFullScreenPlaying == NO) {//将要呈现的画面不是全屏播放页面 - [self.playView resetPlayView]; - } -// self.navigationController.navigationBar.alpha = 1; -} - - - -#pragma mark 基本设置 --(void)setupBasic { - self.tableView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xf0f0f0, 0x000000, 0xfafafa); - - self.navigationController.navigationBar.dk_barTintColorPicker = DKColorPickerWithRGB(0xfa5054,0x444444,0xfa5054); - - self.currentPage = 0; - self.isFullScreenPlaying = NO; -} - -#pragma mark 初始化TableView -- (void)setupTableView { - self.automaticallyAdjustsScrollViewInsets = NO; - self.tableView.contentInset = UIEdgeInsetsMake(CGRectGetMaxY(self.navigationController.navigationBar.frame) + 10, 0, 0, 0); - self.tableView.scrollIndicatorInsets = self.tableView.contentInset; - self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; - [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([VideoTableViewCell class]) bundle:nil] forCellReuseIdentifier:VideoCell]; -} - -#pragma mark 初始化刷新控件 -- (void)setupMJRefreshHeader { - self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(LoadNewData)]; - self.tableView.mj_header.automaticallyChangeAlpha = YES; - [self.tableView.mj_header beginRefreshing]; - - self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(LoadMoreData)]; -} - -#pragma mark 加载最新数据 -- (void)LoadNewData { - TTVideoFetchDataParameter *params = [[TTVideoFetchDataParameter alloc] init]; - TTVideo *firstVideo = self.videoArray.firstObject; - params.recentTime = firstVideo.created_at; - params.page = 0; - params.maxtime = nil; - - [TTDataTool videoWithParameters:params success:^(NSArray *array, NSString *maxtime){ - self.maxtime = maxtime; - self.videoArray = [array mutableCopy]; - [self.tableView reloadData]; - [self.tableView.mj_header endRefreshing]; - } failure:^(NSError *error) { - NSLog(@"%@LoadNewData%@",self,error); - }]; - -} - -#pragma mark 加载更多数据 -- (void)LoadMoreData { - TTVideoFetchDataParameter *parammeters = [[TTVideoFetchDataParameter alloc] init]; - TTVideo *lastVideo = self.videoArray.lastObject; - self.currentPage = self.currentPage+1; - parammeters.page = self.currentPage; - parammeters.remoteTime = lastVideo.created_at; - parammeters.maxtime = self.maxtime; - [TTDataTool videoWithParameters:parammeters success:^(NSArray *array, NSString *maxtime){ - self.maxtime = maxtime; - [self.videoArray addObjectsFromArray:array]; - [self.tableView reloadData]; - [self.tableView.mj_footer endRefreshing]; - } failure:^(NSError *error) { - self.currentPage = self.currentPage-1; - NSLog(@"%@LoadMoreData%@",self,error); - }]; - - -} - -#pragma mark - Table view data source -#pragma mark -UITableViewDataSource 返回tableView有多少组 -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 1; -} - -#pragma mark -UITableViewDataSource 返回tableView每一组有多少行 -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return self.videoArray.count; -} - - -#pragma mark -UITableViewDataSource 返回indexPath对应的cell -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - VideoTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:VideoCell]; - cell.video = self.videoArray[indexPath.row]; - cell.delegate = self; - cell.indexPath = indexPath; - return cell; -} - -#pragma mark -UITableViewDataSource 返回indexPath对应的cell的高度 -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - TTVideo *video = self.videoArray[indexPath.row]; - return video.cellHeight; -} - -#pragma mark -UITableViewDelegate 点击了某个cell --(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - [self pushToVideoCommentViewControllerWithIndexPath:indexPath]; -} - -#pragma mark 点击某个Cell或点击评论按钮跳转到评论页面 --(void)pushToVideoCommentViewControllerWithIndexPath:(NSIndexPath *)indexPath { - VideoCommentViewController *vc = [[VideoCommentViewController alloc] init]; - vc.video = self.videoArray[indexPath.row]; - [self.navigationController pushViewController:vc animated:YES]; -} - -#pragma mark VideoPlayViewDelegate 视频播放时窗口模式与全屏模式切换 -- (void)videoplayViewSwitchOrientation:(BOOL)isFull -{ - if (isFull) { - self.isFullScreenPlaying = YES; - [self presentViewController:self.fullVc animated:YES completion:^{ - self.playView.frame = self.fullVc.view.bounds; - [self.fullVc.view addSubview:self.playView]; - }]; - } else { - [self.fullVc dismissViewControllerAnimated:YES completion:^{ - self.playView.frame = self.currentSelectedCell.video.videoFrame; - [self.currentSelectedCell addSubview:self.playView]; - self.isFullScreenPlaying = NO; - - }]; - - } -} - -#pragma mark - 懒加载代码 -- (FullViewController *)fullVc -{ - if (_fullVc == nil) { - self.fullVc = [[FullViewController alloc] init]; - } - - return _fullVc; -} - - -#pragma mark VideoTableViewCell的代理方法 --(void)clickMoreButton:(TTVideo *)video { - UIAlertController *controller = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - [controller addAction:[UIAlertAction actionWithTitle:@"收藏" style:UIAlertActionStyleDefault handler:nil]]; - [controller addAction:[UIAlertAction actionWithTitle:@"举报" style:UIAlertActionStyleDefault handler:nil]]; - [controller addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; - [self presentViewController:controller animated:YES completion:nil]; -} - -#pragma mark VideoTableViewCell的代理方法 --(void)clickVideoButton:(NSIndexPath *)indexPath { - [self.playView resetPlayView]; - - VideoTableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; - self.currentSelectedCell = cell; - VideoPlayView *playView = [VideoPlayView videoPlayView]; - TTVideo *video = self.videoArray[indexPath.row]; - playView.frame = video.videoFrame; - [cell addSubview:playView]; - cell.playView = playView; - self.playView = playView; - self.playView.delegate = self; - AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:video.videouri]]; - self.playView.playerItem = item; -} - -#pragma mark VideoTableViewCell的代理方法 --(void)clickCommentButton:(NSIndexPath *)indexPath { - [self pushToVideoCommentViewControllerWithIndexPath:indexPath]; -} - --(NSMutableArray *)videoArray { - if (!_videoArray) { - _videoArray = [NSMutableArray array]; - } - return _videoArray; -} - -#pragma mark --UIScrollViewDelegate--scrollView滑动了 --(void)scrollViewDidScroll:(UIScrollView *)scrollView { - if (self.playView.superview && self.isFullScreenPlaying == NO) {//点全屏和退出的时候,也会调用scrollViewDidScroll这个方法 - NSIndexPath *indePath = [self.tableView indexPathForCell:self.currentSelectedCell]; - if (![self.tableView.indexPathsForVisibleRows containsObject:indePath]) {//播放video的cell已离开屏幕 - [self.playView resetPlayView]; - } - } - -// if (self.tableView.contentOffset.y>0) { -// self.navigationController.navigationBar.alpha = 0; -// } else { -// CGFloat yValue = - self.tableView.contentOffset.y;//纵向的差距 -// CGFloat alphValue = yValue/self.tableView.contentInset.top; -// self.navigationController.navigationBar.alpha =alphValue; -// } -} --(void)didReceiveMemoryWarning { - [[SDImageCache sharedImageCache] clearDisk]; - -} - -@end diff --git a/TTNews/Classes/Video/Model/TTVideo.h b/TTNews/Classes/Video/Model/TTVideo.h deleted file mode 100644 index 9955c62..0000000 --- a/TTNews/Classes/Video/Model/TTVideo.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// TTVideo.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -@class TTVideoComment; - -@interface TTVideo : NSObject -@property (nonatomic, copy) NSString *ID; -@property (nonatomic, copy) NSString *profile_image; -@property (nonatomic, copy) NSString *screen_name; -@property (nonatomic, copy) NSString *created_at; -@property (nonatomic, copy) NSString *text; -@property (nonatomic, copy) NSString *love; -@property (nonatomic, copy) NSString *cai; -@property (nonatomic, copy) NSString *repost; -@property (nonatomic, copy) NSString *comment; -@property (nonatomic, copy) NSString *maxtime; -@property (nonatomic, assign, getter=isSina_v) BOOL sina_v; -@property (nonatomic, assign) CGFloat width; -@property (nonatomic, assign) CGFloat height; -@property (nonatomic, copy) NSString *image0; -@property (nonatomic, copy) NSString *image1; -@property (nonatomic, copy) NSString *image2; -@property (nonatomic, copy) NSString *playcount; -@property (nonatomic, copy) NSString *videotime; -@property (nonatomic, copy) NSString *videouri; - -@property (nonatomic, strong) TTVideoComment *top_cmt; -@property (nonatomic, assign) CGFloat cellHeight; -@property (nonatomic, assign) CGRect videoFrame; - - -@end diff --git a/TTNews/Classes/Video/Model/TTVideo.m b/TTNews/Classes/Video/Model/TTVideo.m deleted file mode 100644 index a245861..0000000 --- a/TTNews/Classes/Video/Model/TTVideo.m +++ /dev/null @@ -1,109 +0,0 @@ -// -// TTVideo.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTVideo.h" -#import "TTVideoComment.h" -#import "TTVideoUser.h" -#import -#import "TTConst.h" - -@implementation TTVideo - -+(NSDictionary *)mj_replacedKeyFromPropertyName { - return @{@"ID":@"id", - @"top_cmt":@"top_cmt[0]" - }; -} - --(CGFloat)cellHeight { - if (!_cellHeight) { - - CGSize maxSize = CGSizeMake([UIScreen mainScreen].bounds.size.width - 2*cellMargin, MAXFLOAT); - CGFloat TextHeight = [self.text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15]} context:nil].size.height; - - _cellHeight = cellMargin+cellTextY + TextHeight; - - //videoImageview的高度 - CGFloat videoX = 0; - CGFloat videoY = cellTextY + TextHeight + cellMargin; - CGFloat videoWidth = [UIScreen mainScreen].bounds.size.width; - - CGFloat videoHeight = self.height * videoWidth/self.width; - self.videoFrame = CGRectMake(videoX, videoY, videoWidth, videoHeight); - _cellHeight += videoHeight + cellMargin; - -// 热评的高度 - TTVideoComment *cmt = self.top_cmt; - if (cmt) {//最热评论存在 - NSString *content = [NSString stringWithFormat:@"%@ : %@", cmt.user.username, cmt.content]; - CGFloat topCommentHeight = [content boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13 ]} context:nil].size.height; - - _cellHeight += 0.5*cellMargin + cellTopCommentTopLabelHeight+topCommentHeight + 0.5*cellMargin + cellBottomBarHeight; - } else { - _cellHeight += 0.5*cellMargin+ 0.5*cellMargin +cellBottomBarHeight; - } - - } - - return _cellHeight; -} - --(void)encodeWithCoder:(NSCoder *)aCoder { - [aCoder encodeObject:self.ID forKey:@"ID"]; - [aCoder encodeObject:self.profile_image forKey:@"profile_image"]; - [aCoder encodeObject:self.screen_name forKey:@"screen_name"]; - [aCoder encodeObject:self.created_at forKey:@"created_at"]; - [aCoder encodeObject:self.text forKey:@"text"]; - [aCoder encodeObject:self.love forKey:@"love"]; - [aCoder encodeObject:self.cai forKey:@"cai"]; - [aCoder encodeObject:self.repost forKey:@"repost"]; - [aCoder encodeObject:self.comment forKey:@"comment"]; - [aCoder encodeObject:self.maxtime forKey:@"maxtime"]; - [aCoder encodeBool:self.sina_v forKey:@"sina_v"]; - [aCoder encodeFloat:self.width forKey:@"width"]; - [aCoder encodeFloat:self.height forKey:@"height"]; - [aCoder encodeObject:self.image0 forKey:@"image0"]; - [aCoder encodeObject:self.image1 forKey:@"image1"]; - [aCoder encodeObject:self.image2 forKey:@"image2"]; - [aCoder encodeObject:self.playcount forKey:@"playcount"]; - [aCoder encodeObject:self.videotime forKey:@"videotime"]; - [aCoder encodeObject:self.videouri forKey:@"videouri"]; - [aCoder encodeObject:self.top_cmt forKey:@"top_cmt"]; - [aCoder encodeFloat:self.cellHeight forKey:@"cellHeight"]; - [aCoder encodeCGRect:self.videoFrame forKey:@"videoFrame"]; -} - --(instancetype)initWithCoder:(NSCoder *)aDecoder { - if (self = [super init]) { - self.ID = [aDecoder decodeObjectForKey:@"ID"]; - self.profile_image = [aDecoder decodeObjectForKey:@"profile_image"]; - self.screen_name = [aDecoder decodeObjectForKey:@"screen_name"]; - self.created_at = [aDecoder decodeObjectForKey:@"created_at"]; - self.text = [aDecoder decodeObjectForKey:@"text"]; - self.love = [aDecoder decodeObjectForKey:@"love"]; - self.cai = [aDecoder decodeObjectForKey:@"cai"]; - self.repost = [aDecoder decodeObjectForKey:@"repost"]; - self.comment = [aDecoder decodeObjectForKey:@"comment"]; - self.maxtime = [aDecoder decodeObjectForKey:@"maxtime"]; - self.sina_v = [aDecoder decodeBoolForKey:@"sina_v"]; - self.width = [aDecoder decodeFloatForKey:@"width"]; - self.height = [aDecoder decodeFloatForKey:@"height"]; - self.image0 = [aDecoder decodeObjectForKey:@"image0"]; - self.image1 = [aDecoder decodeObjectForKey:@"image1"]; - self.image2 = [aDecoder decodeObjectForKey:@"image2"]; - self.playcount = [aDecoder decodeObjectForKey:@"playcount"]; - self.videotime = [aDecoder decodeObjectForKey:@"videotime"]; - self.videouri = [aDecoder decodeObjectForKey:@"videouri"]; - self.top_cmt = [aDecoder decodeObjectForKey:@"top_cmt"]; - self.cellHeight = [aDecoder decodeFloatForKey:@"cellHeight"]; - self.videoFrame = [aDecoder decodeCGRectForKey:@"videoFrame"]; - } - return self; -} - -@end diff --git a/TTNews/Classes/Video/Model/TTVideoComment.h b/TTNews/Classes/Video/Model/TTVideoComment.h deleted file mode 100644 index d47d674..0000000 --- a/TTNews/Classes/Video/Model/TTVideoComment.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// TTVideoComment.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -@class TTVideoUser; - -@interface TTVideoComment : NSObject - -@property (nonatomic, copy) NSString *ID;//评论的标识 -@property (nonatomic, copy) NSString *voiceUrl; -/** 音频文件的时长 */ -@property (nonatomic, assign) NSInteger voicetime; -/** 评论的文字内容 */ -@property (nonatomic, copy) NSString *content; -/** 被点赞的数量 */ -@property (nonatomic, assign) NSInteger like_count; -/** 用户 */ -@property (nonatomic, strong) TTVideoUser *user; - - -@end diff --git a/TTNews/Classes/Video/Model/TTVideoComment.m b/TTNews/Classes/Video/Model/TTVideoComment.m deleted file mode 100644 index 0091a8e..0000000 --- a/TTNews/Classes/Video/Model/TTVideoComment.m +++ /dev/null @@ -1,31 +0,0 @@ -// -// TTVideoComment.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTVideoComment.h" -#import - -@implementation TTVideoComment - -+(NSDictionary *)mj_replacedKeyFromPropertyName { - return @{@"ID":@"id"}; -} - - --(void)encodeWithCoder:(NSCoder *)aCoder { - [self mj_encode:aCoder]; -} - --(instancetype)initWithCoder:(NSCoder *)aDecoder { - if (self= [super init]) { - [self mj_decode:aDecoder]; - } - return self; -} - - -@end diff --git a/TTNews/Classes/Video/Model/TTVideoUser.h b/TTNews/Classes/Video/Model/TTVideoUser.h deleted file mode 100644 index fa8c1a9..0000000 --- a/TTNews/Classes/Video/Model/TTVideoUser.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// TTVideoUser.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TTVideoUser : NSObject -@property (nonatomic, copy) NSString *username; -@property (nonatomic, copy) NSString *profile_image; -@property (nonatomic, copy) NSString *sex; -@end diff --git a/TTNews/Classes/Video/Model/TTVideoUser.m b/TTNews/Classes/Video/Model/TTVideoUser.m deleted file mode 100644 index ccd31a5..0000000 --- a/TTNews/Classes/Video/Model/TTVideoUser.m +++ /dev/null @@ -1,16 +0,0 @@ -// -// TTVideoUser.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "TTVideoUser.h" -#import - -@implementation TTVideoUser - -MJCodingImplementation - -@end diff --git a/TTNews/Classes/Video/VideoPlay/FullViewController.h b/TTNews/Classes/Video/VideoPlay/FullViewController.h deleted file mode 100644 index 9c4f52b..0000000 --- a/TTNews/Classes/Video/VideoPlay/FullViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// FullViewController.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface FullViewController : UIViewController - -@end diff --git a/TTNews/Classes/Video/VideoPlay/FullViewController.m b/TTNews/Classes/Video/VideoPlay/FullViewController.m deleted file mode 100644 index e7ed6c9..0000000 --- a/TTNews/Classes/Video/VideoPlay/FullViewController.m +++ /dev/null @@ -1,30 +0,0 @@ -// -// FullViewController.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "FullViewController.h" - -@interface FullViewController () - -@end - -@implementation FullViewController - -- (instancetype)init -{ - if (self = [super init]) { - // self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; - } - return self; -} - -- (UIInterfaceOrientationMask)supportedInterfaceOrientations -{ - return UIInterfaceOrientationMaskLandscapeLeft; -} - -@end diff --git a/TTNews/Classes/Video/VideoPlay/VideoPlayView.h b/TTNews/Classes/Video/VideoPlay/VideoPlayView.h deleted file mode 100644 index 1c19d22..0000000 --- a/TTNews/Classes/Video/VideoPlay/VideoPlayView.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// VideoPlayView.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -#import - -@protocol VideoPlayViewDelegate - -@optional -- (void)videoplayViewSwitchOrientation:(BOOL)isFull; - -@end - -@interface VideoPlayView : UIView - -+ (instancetype)videoPlayView; - -@property (weak, nonatomic) id delegate; - -@property (nonatomic, strong) AVPlayerItem *playerItem; - --(void)suspendPlayVideo; - --(void)resetPlayView; -@end diff --git a/TTNews/Classes/Video/VideoPlay/VideoPlayView.m b/TTNews/Classes/Video/VideoPlay/VideoPlayView.m deleted file mode 100644 index ccd9e0f..0000000 --- a/TTNews/Classes/Video/VideoPlay/VideoPlayView.m +++ /dev/null @@ -1,231 +0,0 @@ -// -// VideoPlayView.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/3. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - - -#import "VideoPlayView.h" - -@interface VideoPlayView() - -/* 播放器 */ -@property (nonatomic, strong) AVPlayer *player; - -// 播放器的Layer -@property (weak, nonatomic) AVPlayerLayer *playerLayer; - -@property (weak, nonatomic) IBOutlet UIImageView *imageView; -@property (weak, nonatomic) IBOutlet UIView *toolView; -@property (weak, nonatomic) IBOutlet UIButton *playOrPauseBtn; -@property (weak, nonatomic) IBOutlet UISlider *progressSlider; -@property (weak, nonatomic) IBOutlet UILabel *timeLabel; -@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *progressView; - -@property (nonatomic, weak) UITableView *tableView; -@property (nonatomic, assign) NSIndexPath *indexPath; - - -// 记录当前是否显示了工具栏 -@property (assign, nonatomic) BOOL isShowToolView; - -/* 定时器 */ -@property (nonatomic, strong) NSTimer *progressTimer; - -#pragma mark - 监听事件的处理 -- (IBAction)playOrPause:(UIButton *)sender; -- (IBAction)switchOrientation:(UIButton *)sender; -- (IBAction)slider; -- (IBAction)startSlider; -- (IBAction)tapAction:(UITapGestureRecognizer *)sender; -- (IBAction)sliderValueChange; - -@end - -@implementation VideoPlayView - -// 快速创建View的方法 -+ (instancetype)videoPlayView -{ - return [[[NSBundle mainBundle] loadNibNamed:@"VideoPlayView" owner:nil options:nil] firstObject]; -} - -- (void)awakeFromNib -{ - [super awakeFromNib]; - self.player = [[AVPlayer alloc] init]; - self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; - [self.imageView.layer addSublayer:self.playerLayer]; - - self.toolView.alpha = 0; - self.isShowToolView = NO; - - [self.progressSlider setThumbImage:[UIImage imageNamed:@"thumbImage"] forState:UIControlStateNormal]; - [self.progressSlider setMaximumTrackImage:[UIImage imageNamed:@"MaximumTrackImage"] forState:UIControlStateNormal]; - [self.progressSlider setMinimumTrackImage:[UIImage imageNamed:@"MinimumTrackImage"] forState:UIControlStateNormal]; - - [self removeProgressTimer]; - [self addProgressTimer]; - - self.playOrPauseBtn.selected = YES; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - - self.playerLayer.frame = self.bounds; -} - -#pragma mark - 设置播放的视频 -- (void)setPlayerItem:(AVPlayerItem *)playerItem -{ - _playerItem = playerItem; - [self.player replaceCurrentItemWithPlayerItem:playerItem]; - [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; - [self.player play]; -} - --(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - AVPlayerItem *item = (AVPlayerItem *)object; - if (item.status == AVPlayerItemStatusReadyToPlay) { - [self.progressView stopAnimating]; - } -} - -// 是否显示工具的View -- (IBAction)tapAction:(UITapGestureRecognizer *)sender { - [UIView animateWithDuration:0.5 animations:^{ - if (self.isShowToolView) { - self.toolView.alpha = 0;//隐藏 - self.isShowToolView = NO; - } else { - self.toolView.alpha = 1; - self.isShowToolView = YES; - } - }]; -} -// --(void)dealloc { - [self.playerItem removeObserver:self forKeyPath:@"status"]; - [self.player replaceCurrentItemWithPlayerItem:nil]; -} -// 暂停按钮的监听 -- (IBAction)playOrPause:(UIButton *)sender { - sender.selected = !sender.selected; - if (sender.selected) { - [self.player play]; - [self addProgressTimer]; - } else { - [self.progressView stopAnimating]; - [self.player pause]; - [self removeProgressTimer]; - } -} -- (void)suspendPlayVideo { - [self.progressView stopAnimating]; - - self.playOrPauseBtn.selected = NO; - self.toolView.alpha = 1; - self.isShowToolView = YES; - - [self.player pause]; - - [self removeProgressTimer]; -} - -#pragma mark - 定时器操作 -- (void)addProgressTimer -{ - self.progressTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateProgressInfo) userInfo:nil repeats:YES]; - [[NSRunLoop mainRunLoop] addTimer:self.progressTimer forMode:NSRunLoopCommonModes]; -} - -- (void)removeProgressTimer -{ - [self.progressTimer invalidate]; - self.progressTimer = nil; -} - -- (void)updateProgressInfo -{ - // 1.更新时间 - self.timeLabel.text = [self timeString]; - - // 2.设置进度条的value - self.progressSlider.value = CMTimeGetSeconds(self.player.currentTime) / CMTimeGetSeconds(self.player.currentItem.duration); -} - -- (NSString *)timeString -{ - NSTimeInterval duration = CMTimeGetSeconds(self.player.currentItem.duration); - NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentTime); - - return [self stringWithCurrentTime:currentTime duration:duration]; -} - -// 切换屏幕的方向 -- (IBAction)switchOrientation:(UIButton *)sender { - sender.selected = !sender.selected; - if ([self.delegate respondsToSelector:@selector(videoplayViewSwitchOrientation:)]) { - [self.delegate videoplayViewSwitchOrientation:sender.selected]; - } -} - -- (IBAction)slider { - [self addProgressTimer]; - NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * self.progressSlider.value; - - // 设置当前播放时间 - [self.player seekToTime:CMTimeMakeWithSeconds(currentTime, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; - - [self.player play]; -} - -- (IBAction)startSlider { - [self removeProgressTimer]; -} - -- (IBAction)sliderValueChange { - NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * self.progressSlider.value; - NSTimeInterval duration = CMTimeGetSeconds(self.player.currentItem.duration); - self.timeLabel.text = [self stringWithCurrentTime:currentTime duration:duration]; -} - -- (NSString *)stringWithCurrentTime:(NSTimeInterval)currentTime duration:(NSTimeInterval)duration -{ - - NSInteger dMin = duration / 60; - NSInteger dSec = (NSInteger)duration % 60; - - NSInteger cMin = currentTime / 60; - NSInteger cSec = (NSInteger)currentTime % 60; - - dMin = dMin<0?0:dMin; - dSec = dSec<0?0:dSec; - cMin = cMin<0?0:cMin; - cSec = cSec<0?0:cSec; - - NSString *durationString = [NSString stringWithFormat:@"%02ld:%02ld", (long)dMin, (long)dSec]; - NSString *currentString = [NSString stringWithFormat:@"%02ld:%02ld", (long)cMin, (long)cSec]; - - return [NSString stringWithFormat:@"%@/%@", currentString, durationString]; -} - --(void)resetPlayView { - [self suspendPlayVideo]; - - [self.playerLayer removeFromSuperlayer]; - // 替换PlayerItem为nil - [self.player replaceCurrentItemWithPlayerItem:nil]; - // 把player置为nil - self.player = nil; - - [self removeFromSuperview]; - - -} - -@end diff --git a/TTNews/Classes/Video/VideoPlay/VideoPlayView.xib b/TTNews/Classes/Video/VideoPlay/VideoPlayView.xib deleted file mode 100644 index 670365a..0000000 --- a/TTNews/Classes/Video/VideoPlay/VideoPlayView.xib +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/Video/View/VideoCommentCell.h b/TTNews/Classes/Video/View/VideoCommentCell.h deleted file mode 100644 index 87d880d..0000000 --- a/TTNews/Classes/Video/View/VideoCommentCell.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// VideoCommentCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -@class TTVideoComment; - -@interface VideoCommentCell : UITableViewCell -/** 评论 */ -@property (nonatomic, strong) TTVideoComment *comment; - -@end diff --git a/TTNews/Classes/Video/View/VideoCommentCell.m b/TTNews/Classes/Video/View/VideoCommentCell.m deleted file mode 100644 index 91be620..0000000 --- a/TTNews/Classes/Video/View/VideoCommentCell.m +++ /dev/null @@ -1,99 +0,0 @@ -// -// VideoCommentCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "VideoCommentCell.h" -#import "TTVideoComment.h" -#import "TTVideoUser.h" -#import -#import -#import - -@interface VideoCommentCell() -@property (weak, nonatomic) IBOutlet UIImageView *profileImageView; -@property (weak, nonatomic) IBOutlet UIImageView *sexView; -@property (weak, nonatomic) IBOutlet UILabel *contentLabel; -@property (weak, nonatomic) IBOutlet UILabel *usernameLabel; -@property (weak, nonatomic) IBOutlet UILabel *likeCountLabel; -@property (weak, nonatomic) IBOutlet UIButton *voiceButton; -@end - -@implementation VideoCommentCell - -- (BOOL)canBecomeFirstResponder -{ - return YES; -} - -- (BOOL)canPerformAction:(SEL)action withSender:(id)sender -{ - return NO; -} - -- (void)awakeFromNib -{ - [super awakeFromNib]; - - UIImageView *bgView = [[UIImageView alloc] init]; - bgView.image = [UIImage imageNamed:@"mainCellBackground"]; - self.backgroundView = bgView; - self.contentView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - self.contentLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.usernameLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.likeCountLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - [SDWebImageManager sharedManager].delegate = self; - -// self.profileImageView.layer.cornerRadius = self.profileImageView.width * 0.5; -// self.profileImageView.layer.masksToBounds = YES; -} - -- (void)setComment:(TTVideoComment *)comment -{ - _comment = comment; - - [self.profileImageView sd_setImageWithURL:[NSURL URLWithString:comment.user.profile_image] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"] options:SDWebImageTransformAnimatedImage]; - - - - self.sexView.image = [comment.user.sex isEqualToString:@"m"] ? [UIImage imageNamed:@"Profile_manIcon"] : [UIImage imageNamed:@"Profile_womanIcon"]; - self.contentLabel.text = comment.content; - self.usernameLabel.text = comment.user.username; - self.likeCountLabel.text = [NSString stringWithFormat:@"%zd", comment.like_count]; - - if (comment.voiceUrl.length) { - self.voiceButton.hidden = NO; - [self.voiceButton setTitle:[NSString stringWithFormat:@"%zd''", comment.voicetime] forState:UIControlStateNormal]; - } else { - self.voiceButton.hidden = YES; - } -} -- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL { - - // NO代表透明 - UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0); - - // 获得上下文 - CGContextRef context = UIGraphicsGetCurrentContext(); - - // 添加一个圆 - CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height); - CGContextAddEllipseInRect(context, rect); - - // 裁剪 - CGContextClip(context); - - // 将图片画上去 - [image drawInRect:rect]; - - UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - - return resultImage; -} - -@end diff --git a/TTNews/Classes/Video/View/VideoCommentCell.xib b/TTNews/Classes/Video/View/VideoCommentCell.xib deleted file mode 100644 index 0193c35..0000000 --- a/TTNews/Classes/Video/View/VideoCommentCell.xib +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Classes/Video/View/VideoTableViewCell.h b/TTNews/Classes/Video/View/VideoTableViewCell.h deleted file mode 100644 index b6d307f..0000000 --- a/TTNews/Classes/Video/View/VideoTableViewCell.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// VideoTableViewCell.h -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -#import "VideoPlayView.h" -@class VideoPlayView; -@class TTVideo; - -@protocol VideoTableViewCellDelegate - -@optional - --(void)clickMoreButton:(TTVideo *)video; --(void)clickVideoButton:(NSIndexPath *)indexPath; --(void)clickCommentButton:(NSIndexPath *)indexPath; - -@end - -@interface VideoTableViewCell : UITableViewCell - -+(instancetype)cell; - -@property (nonatomic, strong) TTVideo *video; -@property (nonatomic, weak) id delegate; -@property (nonatomic, strong) NSIndexPath *indexPath; -@property (nonatomic, weak) VideoPlayView *playView; - -@end diff --git a/TTNews/Classes/Video/View/VideoTableViewCell.m b/TTNews/Classes/Video/View/VideoTableViewCell.m deleted file mode 100644 index f2df9b6..0000000 --- a/TTNews/Classes/Video/View/VideoTableViewCell.m +++ /dev/null @@ -1,196 +0,0 @@ -// -// VideoTableViewCell.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/2. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import "VideoTableViewCell.h" -#import "TTVideo.h" -#import "TTVideoComment.h" -#import "TTVideoUser.h" -#import "VideoPlayView.h" -#import -#import -#import - -@interface VideoTableViewCell() -@property (weak, nonatomic) IBOutlet UIImageView *headerImageView; -@property (weak, nonatomic) IBOutlet UILabel *nameLabel; -@property (weak, nonatomic) IBOutlet UILabel *createdTimeLabel; -@property (weak, nonatomic) IBOutlet UIButton *AddFriendsButton; -@property (weak, nonatomic) IBOutlet UIButton *loveButton; -@property (weak, nonatomic) IBOutlet UIButton *hatebutton; -@property (weak, nonatomic) IBOutlet UIButton *repostButton; -@property (weak, nonatomic) IBOutlet UIButton *commentButton; -@property (weak, nonatomic) IBOutlet UILabel *contentLabel; -@property (weak, nonatomic) IBOutlet UIImageView *vipImageView; -@property (weak, nonatomic) IBOutlet UILabel *topCommentTopLabel; -@property (weak, nonatomic) IBOutlet UIImageView *videoImageView; -@property (weak, nonatomic) IBOutlet UILabel *playCountLabel; -@property (weak, nonatomic) IBOutlet UILabel *timelabel; -@property (weak, nonatomic) IBOutlet UILabel *topCommentLabel; -@property (weak, nonatomic) IBOutlet UIView *VideoContianerView; - -@property (weak, nonatomic) IBOutlet UIView *separatorLine1; -@property (weak, nonatomic) IBOutlet UIView *separatorLine2; -@property (weak, nonatomic) IBOutlet UIView *separatorLine3; -@property (weak, nonatomic) IBOutlet UIView *separatorLine4; - -@end -@implementation VideoTableViewCell - - -+(instancetype)cell { - return [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject]; -} -- (void)awakeFromNib { - [super awakeFromNib]; - - self.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - - self.contentView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa); - self.nameLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.contentLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.createdTimeLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.timelabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.topCommentTopLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - self.topCommentLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT); - - self.separatorLine1.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); - self.separatorLine2.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); - self.separatorLine3.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); - self.separatorLine4.dk_backgroundColorPicker = DKColorPickerWithKey(SEP); - - - self.nameLabel.textColor = [UIColor colorWithRed:243/255.0 green:75/255.0 blue:80/255.0 alpha:1.0]; - UIImageView *imageView = [[UIImageView alloc] init]; - imageView.image = [UIImage imageNamed:@"mainCellBackground"]; - self.backgroundView = imageView; - [SDWebImageManager sharedManager].delegate = self; -} - -- (void)setVideo:(TTVideo *)video { - _video = video; - - [self.headerImageView sd_setImageWithURL:[NSURL URLWithString:video.profile_image] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"] options:SDWebImageTransformAnimatedImage]; - - self.nameLabel.text = video.screen_name; - self.createdTimeLabel.text = video.created_at; - self.contentLabel.text = video.text; - self.vipImageView.hidden = !video.isSina_v; - - NSInteger count = video.playcount.integerValue; - if (count>10000) { - self.playCountLabel.text = [NSString stringWithFormat:@"%ld万播放",count/10000]; - } else { - self.playCountLabel.text = [NSString stringWithFormat:@"%ld播放",(long)count]; - } - NSInteger time = video.videotime.integerValue; - self.timelabel.text = [NSString stringWithFormat:@"%02ld%02ld",time/60,time%60]; - - [self.videoImageView sd_setImageWithURL:[NSURL URLWithString:video.image1]]; - - [self setupButton:self.loveButton WithTittle:video.love]; - [self setupButton:self.hatebutton WithTittle:video.cai]; - [self setupButton:self.repostButton WithTittle:video.repost]; - [self setupButton:self.commentButton WithTittle:video.comment]; - - - TTVideoComment *comment = video.top_cmt; - if(comment) { - self.topCommentLabel.text = [NSString stringWithFormat:@"%@ : %@",comment.user.username, comment.content]; - self.topCommentTopLabel.text = @"最热评论"; - } else { - self.topCommentLabel.text = @""; - self.topCommentTopLabel.text = @""; - } - - -} - -- (void)setupButton:(UIButton *)button WithTittle:(NSString *)tittle { - double number = tittle.doubleValue; - if (number > 10000) { - [button setTitle:[NSString stringWithFormat:@"%.1f万",number/10000] forState:UIControlStateNormal]; - return; - } - [button setTitle:tittle forState:UIControlStateNormal]; - -} - -- (void)setSelected:(BOOL)selected animated:(BOOL)animated { - [super setSelected:selected animated:animated]; -} - -- (IBAction)playVideo:(id)sender { - if ([self.delegate respondsToSelector:@selector(clickVideoButton:)]) { - [self.delegate clickVideoButton:self.indexPath]; - } -} - -- (IBAction)more:(id)sender { - - if ([self.delegate respondsToSelector:@selector(clickMoreButton:)]) { - [self.delegate clickMoreButton:self.video]; - } - NSLog(@"%@-----%@",NSStringFromCGRect(self.topCommentTopLabel.frame), NSStringFromCGRect(self.topCommentLabel.frame)); -} - -- (void)setFrame:(CGRect)frame { - static CGFloat margin = 10; - - frame.size.width = [UIScreen mainScreen].bounds.size.width; - frame.size.height = self.video.cellHeight - margin; - [super setFrame:frame]; -} - -- (IBAction)love:(id)sender { - UIButton *button = (UIButton *)sender; - button.selected = !button.selected; -} - -- (IBAction)hate:(id)sender { - UIButton *button = (UIButton *)sender; - button.selected = !button.selected; -} - -- (IBAction)repost:(id)sender { - -} - -- (IBAction)comment:(id)sender { - if ([self.delegate respondsToSelector:@selector(clickCommentButton:)]) { - [self.delegate clickCommentButton:self.indexPath]; - } -} - -- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL { - - // NO代表透明 - UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0); - - // 获得上下文 - CGContextRef context = UIGraphicsGetCurrentContext(); - - // 添加一个圆 - CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height); - CGContextAddEllipseInRect(context, rect); - - // 裁剪 - CGContextClip(context); - - // 将图片画上去 - [image drawInRect:rect]; - - UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - - return resultImage; -} - - - -@end diff --git a/TTNews/Classes/Video/View/VideoTableViewCell.xib b/TTNews/Classes/Video/View/VideoTableViewCell.xib deleted file mode 100644 index bcb61cd..0000000 --- a/TTNews/Classes/Video/View/VideoTableViewCell.xib +++ /dev/null @@ -1,396 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/Info.plist b/TTNews/Info.plist deleted file mode 100644 index 028aa2b..0000000 --- a/TTNews/Info.plist +++ /dev/null @@ -1,50 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/TTNews/LaunchScreen.xib b/TTNews/LaunchScreen.xib deleted file mode 100644 index 9127585..0000000 --- a/TTNews/LaunchScreen.xib +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TTNews/NewsURLs.plist b/TTNews/NewsURLs.plist deleted file mode 100755 index 63cdb6a..0000000 --- a/TTNews/NewsURLs.plist +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - title - 头条 - urlString - headline/T1348647853363 - replyUrl - 3g_bbs - - - title - NBA - urlString - list/T1348649145984 - replyUrl - sports_nba_bbs - - - title - 手机 - urlString - list/T1348649654285 - replyUrl - mobile_bbs - - - title - 移动互联 - urlString - list/T1351233117091 - replyUrl - mobile_bbs - - - title - 娱乐 - urlString - list/T1348648517839 - replyUrl - auto_bbs - - - title - 时尚 - urlString - list/T1348650593803 - replyUrl - lady_bbs - - - title - 电影 - urlString - list/T1348648650048 - replyUrl - ent2_bbs - - - title - 科技 - urlString - list/T1348649580692 - replyUrl - tech_bbs - - - diff --git a/TTNews/main.m b/TTNews/main.m deleted file mode 100644 index 4cc675a..0000000 --- a/TTNews/main.m +++ /dev/null @@ -1,20 +0,0 @@ -// -// main.m -// TTNews -// -// Created by 瑞文戴尔 on 16/4/20. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - - } - - -} diff --git a/TTNewsTests/Info.plist b/TTNewsTests/Info.plist deleted file mode 100644 index ba72822..0000000 --- a/TTNewsTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/TTNewsTests/TTNewsTests.m b/TTNewsTests/TTNewsTests.m deleted file mode 100644 index 21aede6..0000000 --- a/TTNewsTests/TTNewsTests.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// TTNewsTests.m -// TTNewsTests -// -// Created by 瑞文戴尔 on 16/4/20. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TTNewsTests : XCTestCase - -@end - -@implementation TTNewsTests - -- (void)setUp { - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - -- (void)testExample { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. -} - -- (void)testPerformanceExample { - // This is an example of a performance test case. - [self measureBlock:^{ - // Put the code you want to measure the time of here. - }]; -} - -@end diff --git a/TTNewsUITests/Info.plist b/TTNewsUITests/Info.plist deleted file mode 100644 index ba72822..0000000 --- a/TTNewsUITests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/TTNewsUITests/TTNewsUITests.m b/TTNewsUITests/TTNewsUITests.m deleted file mode 100644 index c8e48c2..0000000 --- a/TTNewsUITests/TTNewsUITests.m +++ /dev/null @@ -1,40 +0,0 @@ -// -// TTNewsUITests.m -// TTNewsUITests -// -// Created by 瑞文戴尔 on 16/4/20. -// Copyright © 2016年 瑞文戴尔. All rights reserved. -// - -#import - -@interface TTNewsUITests : XCTestCase - -@end - -@implementation TTNewsUITests - -- (void)setUp { - [super setUp]; - - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - self.continueAfterFailure = NO; - // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. - [[[XCUIApplication alloc] init] launch]; - - // 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. -} - -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - -- (void)testExample { - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. -} - -@end diff --git a/_sidebar.md b/_sidebar.md new file mode 100644 index 0000000..bd331c5 --- /dev/null +++ b/_sidebar.md @@ -0,0 +1,35 @@ +- [首页](README.md) +* Java + - [基础](docs/JavaBasic.md) + * 容器 + - [ArrayList和LinkedList](docs/ArrayList.md) + - [HashMap和ConcurrentHashMap](docs/HashMap.md) + - [多线程](docs/JavaMultiThread.md) + - [锁相关](docs/Lock.md) +* Redis + - [基础](docs/RedisBasic.md) + - [数据结构](docs/RedisDataStruct.md) + - [持久化(AOF和RDB)](docs/RedisStore.md) + - [高可用(主从切换和哨兵机制)](docs/RedisUserful.md) +* MySQL + - [基础](docs/MySQLNote.md) + - [慢查询优化实践](docs/MySQLWork.md) +* JVM + - [基础](docs/JavaJVM.md) +- [Kafka](docs/Kafka.md) +- [ZooKeeper](docs/ZooKeeper.md) +- [HTTP](docs/HTTP.md) +- [Spring](docs/Spring.md) +- [Nginx](docs/Nginx.md) +- [系统设计](docs/SystemDesign.md) +* 算法 + - [《剑指Offer》解题思考](docs/CodingInterviews.md) + - [《LeetCode热门100题》解题思考(上)](docs/LeetCode.md) + - [《LeetCode热门100题》解题思考(下)](docs/LeetCode1.md) +- [大厂面试公众号文章系列](docs/BATInterview.md) +* 读书笔记 + - [《Redis设计与实现》读书笔记 上](docs/RedisBook1.md) + - [《Redis设计与实现》读书笔记 下](docs/RedisBook2.md) + - [《MySQL必知必会》读书笔记](docs/MySQLBook1.md) + - [《深入理解Java虚拟机-第三版》读书笔记](docs/JVMBook.md) +- [好书推荐](docs/bookRecommend.md) diff --git a/docs/ArrayList.md b/docs/ArrayList.md new file mode 100644 index 0000000..d2717bb --- /dev/null +++ b/docs/ArrayList.md @@ -0,0 +1,545 @@ +(PS:建了一个技术微信群,可以自由地讨论技术,工作和生活,也会分享一些我自己在看的技术资料,不定时发放红包福利,欢迎大家扫[首页里面的二维码](README.md)进群,希望和大家一起学习进步!大家如果想一起为这个项目做贡献的话,也可以进群大家聊一聊) + +下面是主要是自己看了很多Java容器类相关的博客,以及很多面经中涉及到的Java容器相关的面试题后,自己全部手写的解答,也画了一些流程图,之后会继续更新这一部分。 + +#### [1.ArrayList与LinkedList的区别是什么?](#ArrayList与LinkedList的区别是什么?) + +#### [2.怎么使ArrayList,LinkedList变成线程安全的呢?](#怎么使ArrayList,LinkedList变成线程安全的呢?) + +#### [3.ArrayList遍历时删除元素有哪些方法?](#ArrayList遍历时删除元素有哪些方法?) +#### [4.ConcurrentModificationException是什么?](#ConcurrentModificationException是什么?) + +#### [5.java容器类的层次是怎么样的?](#java容器类的层次是怎么样的?) + +### ArrayList与LinkedList的区别是什么? + +每次遇到一个好看的小姐姐,我们一般都是会去看她的外貌,身材,大小(咳咳,我们指的是年龄大小)等等。同样的,我们在分析Java容器之间的区别时,我们也可以从继承树,底层数据结构,线程安全,执行效率来进行分析。 + +#### 1.底层使用的数据结构 + +* ArrayList 底层使用的是**Object数组**,初始化时就会指向的会是一个static修饰的空数组,数组长度一开始为**0**,插入第一个元素时数组长度会初始化为**10**,之后每次数组空间不够进行扩容时都是增加为原来的**1.5倍**。ArrayList的空间浪费主要体现在在list列表的结尾会预留一定的容量空间(为了避免添加元素时,数组空间不够频繁申请内存),而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放后继指针next和前驱指针pre以及数据) + +* LinkedList 底层使用的数据结构是**双向链表**,每个节点保存了指向前驱节点和后继结点的指针。初始化时,不执行任何操作,添加第一个元素时,再去构造链表中的节点。 + +#### 2.是否保证线程安全: + + ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全。 + +因为ArrayList的插入元素的方法就是裸奔的,直接将原数组index及后面的元素拷贝到index+1及后面的位置上,然后将index位置设置为插入的值,并发修改时保证不了数据安全性,所以也不允许并发修改,一旦检测到并发修改,会抛出ConcurrentModificationException异常。 + +```java +//ArrayList的插入元素的方法 +public void add(int index, E element) { + rangeCheckForAdd(index); + ensureCapacityInternal(size + 1); // Increments modCount!! + System.arraycopy(elementData, index, elementData, index + 1, + size - index);//将原数组index之后的元素拷贝到原数组index+1后面的位置上 + elementData[index] = element; + size++; +} +``` + +#### 3.插入和删除的复杂度: + +* ArrayList 采用数组存储,元素的物理存储地址是连续的,支持以O(1)的时间复杂度对元素快速访问。插入和删除元素后,需要将后面的元素进行移动,所以插入和删除元素的时间复杂度受元素位置的影响。复杂度是 O(n), +* LinkedList 采用链表存储,所以不能快速随机访问。所以首尾插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)(如果是插入到中间位置还需要考虑寻找插入位置的时间复杂度)。而数组为近似 O(n)。 + +#### 4.继承树 + +* ArrayList继承于AbstractList抽象类,实现了**List, RandomAccess, Cloneable, java.io.Serializable**接口 。 +* LinkedList继承自AbstractSequentialList 实现**List, Deque, Cloneable, java.io.Serializable**接口。 + +**AbstractSequentialList**是AbstractList类的子类,实现了根据下标来访问元素的一些方法,主要是通过listIterator遍历获取特定元素。 + +**List接口**代表的是有序结合,与Set相反,List的元素是按照移动的顺序进行排列。 + +**Cloneable接口**代表类会重新父类Object的clone()方法,支持对实例对象的clone操作。 + +**java.io.Serializable**接口代表类支持序列化。 + +**RandomAccess**是一个标示性接口,代表ArrayList支持快速访问,而LinkedList不支持。 + +**Deque**接口是双端队列的意思,代表LinkedList支持两端元素插入和移除。 + +### 怎么使ArrayList,LinkedList变成线程安全的呢? + +#### 1.使用SynchronizedList + +SynchronizedList是一个线程安全的包装类。继承于SynchronizedCollection,SynchronizedCollection实现了Collection接口,SynchronizedList包含一个List对象,对List的访问修改方法进行了一些封装,在封装的方法中会对list使用同步锁加锁,然后再进行存取和修改操作。 + +使用方法如下 + +```java +LinkedList linkedList = new LinkedList(); +//调用Collections的synchronizedList方法,传入一个linkedList,会返回一个SynchronizedList实例对象 +List synchronizedList = Collections.synchronizedList(linkedList); + +//调用Collections的synchronizedList方法,ArrayList,返回一个SynchronizedRandomAccessList实例对象 +ArrayList arrayList = new ArrayList(); +List synchronizedRandomAccessList = Collections.synchronizedList(linkedList); +``` + +(Collections.synchronizedList()方法会判断传入的对象是否实现了 RandomAccess接口,是的话,会返回一个SynchronizedRandomAccessList对象,SynchronizedRandomAccessList是SynchronizedList的子类,只是会多一个以线程安全的方式获取子数组的方法)。 + +SynchronizedList类的部分代码如下: + +```java +static class SynchronizedList + extends SynchronizedCollection + implements List { + final List list;//源list + final Object mutex; + + SynchronizedCollection(Collection c) { + this.c = Objects.requireNonNull(c); + mutex = this;//mutex就是SynchronizedList实例自己,作为同步锁使用 + } + + public E get(int index) { + synchronized (mutex) { + 是父类中的成员变量,在父类中会将list赋值给mutex + return list.get(index); + } + } + + public E set(int index, E element) { + synchronized (mutex) {return list.set(index, element);} + } +} +``` + +#### 2.使用CopyOnWriteArrayList + +CopyOnWriteArrayList跟ArrayList类似,都是实现了List接口,只不过它的父类是Object,而不是AbstractList。CopyOnWriteArrayList与ArrayList的不同在于, + +##### 1.内部持有一个ReentrantLock类型的lock锁,用于控制并发访问 + +```java + final transient ReentrantLock lock = new ReentrantLock(); +``` + +在对数组进行修改的方法中,都会先获取lock,获取成功才能进行修改,修改完释放锁,保证每次只允许一个线程对数组进行修改。 + +##### 2.使用volatile修饰Object数组,使得变量具备内存可见性 + +```java + //CopyOnWriteArrayList + private transient volatile Object[] array; + + //ArrayList + private transient Object[] elementData;//transient +``` + +可以看到区别主要在于CopyOnWriteArrayList的Object是使用volatile来修饰的,volatile可以使变量具备内存可见性,一个线程在工作内存中对变量进行修改后,会立即更新到物理内存,并且使得其他线程中的这个变量缓存失效,其他线程在读取会去物理内存中读取最新的值。(volatile修饰的是指向数组的引用变量,所以对数组添加元素,删除元素不会改变引用,只有对数组变量array重新赋值才会改变。所以为了保证内存可见性,CopyOnWriteArrayList.add()方法在添加元素时,都是复制出一个新数组,进行修改操作后,再设置到就数组上) + +注意事项:Object数组都使用transient修饰是**因为transient修饰的属性不会参与序列化**,ArrayList通过实现writeObject()和readObject()方法来自定义了序列化方法(基于反序列化时节约空间考虑,如果用默认的序列方法,源elementData数组长度为100,实际只有10个元素,反序列化时也会分配长度为100的数组,造成内存浪费。) + +**下面是CopyOnWriteArrayList的add()方法:** + +```java +public boolean add(E e) { + final ReentrantLock lock = this.lock; + //1. 使用Lock,保证写线程在同一时刻只有一个 + lock.lock(); + try { + //2. 获取旧数组引用 + Object[] elements = getArray(); + int len = elements.length; + //3. 创建新的数组,并将旧数组的数据复制到新数组中 + Object[] newElements = Arrays.copyOf(elements, len + 1); + //4. 往新数组中添加新的数据 + newElements[len] = e; + //5. 将旧数组引用指向新的数组 + setArray(newElements); + return true; + } finally { + lock.unlock(); + } +} +``` + +#### SynchronizedList和CopyOnWriteArrayList优缺点 + +##### SynchronizedList:读写都加锁 + +SynchronizedList是通过对读写方法使用synchronized修饰来实现同步的,即便只是多个线程在读数据,也不能进行,如果是读比较多的场景下,会性能不高,所以适合读写均匀的情况。 + +##### CopyOnWriteArrayList:读不加锁,写加锁 + +而CopyOnWriteArrayList是读写分离的,只对写操作加锁,但是每次写操作(添加和删除元素等)时都会复制出一个新数组,完成修改后,然后将新数组设置到旧数组的引用上,所以在写比较多的情况下,会有很大的性能开销,所以适合读比较多的应用场景。 + +### ArrayList遍历时删除元素有哪些方法? +首先结论如下: + +第1种方法 - 普通for循环正序删除(结果:会漏掉元素判断) + +第2种方法 - 普通for循环倒序删除(结果:正确删除) + +第3种方法 - for-each循环删除(结果:抛出异常) + +第4种方法 - Iterator遍历,使用ArrayList.remove()删除元素(结果:抛出异常) + +第5种方法 - Iterator遍历,使用Iterator的remove删除元素(结果:正确删除) + +下面让我们来详细探究一下原因吧! + + +首先初始化一个数组arrayList,假设我们要删除等于3的元素。 +```java + public static void main(String[] args) { + ArrayList arrayList = new ArrayList(); + arrayList.add(1); + arrayList.add(2); + arrayList.add(3); + arrayList.add(3); + arrayList.add(4); + arrayList.add(5); + removeWayOne(arrayList); + } +``` + +#### 第1种方法 - 普通for循环正序删除(结果:会漏掉对后一个元素的判断) + +```java +for (int i = 0; i < arrayList.size(); i++) { + if (arrayList.get(i) == 3) {//3是要删除的元素 + arrayList.remove(i); + //解决方案: 加一行代码i = i - 1; 删除元素后,下标减1 + } + System.out.println("当前arrayList是"+arrayList.toString()); +} +//原ArrayList是[1, 2, 3, 3, 4, 5] +//删除后是[1, 2, 3, 4, 5] +``` +输出结果: +``` +当前arrayList是[1, 2, 3, 3, 4, 5] +当前arrayList是[1, 2, 3, 3, 4, 5] +当前arrayList是[1, 2, 3, 4, 5] +当前arrayList是[1, 2, 3, 4, 5] +当前arrayList是[1, 2, 3, 4, 5] +``` +可以看到少删除了一个3, + +原因在于调用remove删除元素时,remove方法调用System.arraycopy()方法将后面的元素移动到前面的位置,也就是第二个3会移动到数组下标为2的位置,而在下一次循环时,i+1之后,i会为3,不会对数组下标为2这个位置进行判断,所以这种写法,在删除元素时,被删除元素a的后一个元素b会移动a的位置,而i已经加1,会忽略对元素b的判断,所以如果是连续的重复元素,会导致少删除。 + +##### 解决方案 + +可以在删除元素后,执行i=i-1,使得下次循环时再次对该数组下标进行判断。 + +#### 第2种方法 - 普通for循环倒序删除(结果:正确删除) + +```java + for (int i = arrayList.size() -1 ; i>=0; i--) { + if (arrayList.get(i).equals(3)) { + arrayList.remove(i); + } + System.out.println("当前arrayList是"+arrayList.toString()); +} +``` +输出结果: +``` +当前arrayList是[1, 2, 3, 3, 4, 5] +当前arrayList是[1, 2, 3, 3, 4, 5] +当前arrayList是[1, 2, 3, 4, 5] +当前arrayList是[1, 2, 4, 5] +当前arrayList是[1, 2, 4, 5] +当前arrayList是[1, 2, 4, 5] +``` + +这种方法可以正确删除元素,因为调用remove删除元素时,remove方法调用System.arraycopy()将被删除元素a后面的元素向前移动,而不会影响元素a之前的元素,所以倒序遍历可以正常删除元素。 + +### 第3种方法 - for-each循环删除(结果:抛出异常) + +抛出异常的根本原因在于for-each是使用Iterator来实现遍历的,调用ArrayList.remove()方法会将modCount+1,而Iterator内部的expectedModCount确没有更新,这样在进行下次循环时调用Iterator.next()会对modCount和expectedModCount进行比较,不一致就会抛出ConcurrentModificationException异常。 + +```java +public static void removeWayThree(ArrayList arrayList) { + for (Integer value : arrayList) { + if (value.equals(3)) {//3是要删除的元素 + arrayList.remove(value); + } + System.out.println("当前arrayList是"+arrayList.toString()); + } +} +``` +输出结果: +```java +当前arrayList是[1, 2, 3, 3, 4, 5] +当前arrayList是[1, 2, 3, 3, 4, 5] +当前arrayList是[1, 2, 3, 4, 5] +Exception in thread "main" java.util.ConcurrentModificationException + at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) + at java.util.ArrayList$Itr.next(ArrayList.java:851) + at com.test.ArrayListTest1.removeWayThree(ArrayListTest1.java:50) + at com.test.ArrayListTest1.main(ArrayListTest1.java:24) +``` + +会抛出ConcurrentModificationException异常,主要在于for-each的底层实现是使用ArrayList.iterator的hasNext()方法和next()方法实现的,我们可以使用反编译进行验证,对包含上面的方法的类使用以下命令反编译验证 + +```java +javac ArrayTest.java//生成ArrayTest.class文件 +javap -c ArrayListTest.class//对class文件反编译 +``` + +得到removeWayThree方法的反编译代码如下: + +```java + public static void removeWayThree(java.util.ArrayList); + Code: + 0: aload_0 + 1: invokevirtual #12 // Method java/util/ArrayList.iterator:()Ljava/util/Iterator; + 4: astore_1 + 5: aload_1 + 6: invokeinterface #13, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 调用Iterator.hasNext()方法 + 11: ifeq 44 + 14: aload_1 + 15: invokeinterface #14, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;调用Iterator.next()方法 + 20: checkcast #9 // class java/lang/Integer + 23: astore_2 + 24: aload_2 + 25: iconst_3 + 26: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 29: invokevirtual #10 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z + 32: ifeq 41 + 35: aload_0 + 36: aload_2 + 37: invokevirtual #15 // Method java/util/ArrayList.remove:(Ljava/lang/Object;)Z + 40: pop + 41: goto 5 + 44: return +``` + +可以很清楚得看到Iterator.hasNext()来判断是否还有下一个元素,和Iterator.next()方法来获取下一个元素。而因为在删除元素时,remove()方法会调用fastRemove()方法,其中会对modCount+1,代表对数组进行了修改,将修改次数+1。 + +```java + public boolean remove(Object o) { + if (o == null) { + for (int index = 0; index < size; index++) + if (elementData[index] == null) { + fastRemove(index); + return true; + } + } else { + for (int index = 0; index < size; index++) + if (o.equals(elementData[index])) { + fastRemove(index); + return true; + } + } + return false; +} + +private void fastRemove(int index) { + modCount++; + int numMoved = size - index - 1; + if (numMoved > 0) + System.arraycopy(elementData, index+1, elementData, index,numMoved); + elementData[--size] = null; // clear to let GC do its work +} +``` + +而当删除完元素后,进行下一次循环时,会调用下面源码中Itr.next()方法获取下一个元素,会调用checkForComodification()方法对ArrayList进行校验,判断在遍历ArrayList是否已经被修改,由于之前对modCount+1,而**Iterator中的expectedModCount**还是初始化时ArrayList.Itr对象时赋的值,所以会不相等,然后抛出ConcurrentModificationException异常。 + +##### 那么有什么办法可以让expectedModCount及时更新呢? + +可以看到下面Itr的源码中,在Itr.remove()方法中删除元素后会对 expectedModCount更新,所以我们在使用删除元素时使用Itr.remove()方法来删除元素就可以保证expectedModCount的更新了,具体看**第5种**方法。 + +```java +//使用Iterator遍历元素的方法 +/* +Iterator遍历时使用next()方法返回下一个元素,主要通过将游标cursor+1,获得下一个元素 + +调用remove()删除元素时,主要删除lastRet下标对应的元素,并且将cursor设置为lastRet的值,因为后面的元素向前面的空位移动了一位 + +Iterator遍历过程中,在一次循环中也不能多次调用remove()方法,因为每次remove()后就会将lastRet设置为-1,本次循环中再remove就会抛异常,必须等调用next()方法后对lastRet重新赋值。 +*/ +public void tranverse() { + ArrayList list = new ArrayList(); + list.add(1); + list.add(2); + list.add(3); + list.add(3); + list.add(4); + list.add(5); + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + Integer value = iterator.next(); + System.out.println(value); + if (value.equals(3)) { + iterator.remove(); + iterator.remove();//在循环中多次调用iterator的remove方法会抛出异常 + iterator.remove(); + } + } +} +``` + +**Iterator的源代码** + +```java +private class Itr implements Iterator { + int cursor; // 游标 + int lastRet = -1; // index of last element returned; -1 if no such + int expectedModCount = modCount;//期待的modCount值 + + public boolean hasNext() { + return cursor != size; + } + + @SuppressWarnings("unchecked") + public E next() { + checkForComodification();//判断expectedModCount与当前的modCount是否一致 + int i = cursor; + if (i >= size) + throw new NoSuchElementException(); + Object[] elementData = ArrayList.this.elementData; + if (i >= elementData.length) + throw new ConcurrentModificationException(); + cursor = i + 1; + return (E) elementData[lastRet = i]; + } + + public void remove() { + if (lastRet < 0) + throw new IllegalStateException(); + checkForComodification(); + try { + ArrayList.this.remove(lastRet); + cursor = lastRet; + lastRet = -1; + expectedModCount = modCount;//更新expectedModCount + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + + final void checkForComodification() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } +``` + +#### 第4种方法 - Iterator遍历,使用ArrayList.remove()删除元素(结果:抛出异常) + +```java +Iterator iterator = arrayList.iterator(); +while (iterator.hasNext()) { + Integer value = iterator.next(); + if (value.equals(3)) {//3是要删除的元素 + arrayList.remove(value); + } + System.out.println("当前arrayList是"+arrayList.toString()); +} +``` +输出结果: +```java +当前arrayList是[1, 2, 3, 3, 4, 5] +当前arrayList是[1, 2, 3, 3, 4, 5] +当前arrayList是[1, 2, 3, 4, 5] +Exception in thread "main" java.util.ConcurrentModificationException + at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) + at java.util.ArrayList$Itr.next(ArrayList.java:851) + at com.test.ArrayListTest1.removeWayFour(ArrayListTest1.java:61) + at com.test.ArrayListTest1.main(ArrayListTest1.java:25) +``` + +第4种方法其实是第3种方法在编译后的代码,所以第四种写法也会抛出ConcurrentModificationException异常。这种需要注意的是,每次调用iterator的next()方法,会导致游标向右移动,从而达到遍历的目的。所以在单次循环中不能多次调用next()方法,不然会导致每次循环时跳过一些元素,我在一些博客里面看到了一些错误的写法,比如这一篇[《在ArrayList的循环中删除元素,会不会出现问题?》](https://juejin.im/post/5b92844a6fb9a05d290ed46c)文章中: + +![image-20200101124822998](../static/image-20200101124822998.png) + +先调用iterator.next()获取元素,与elem进行比较,如果相等,再调用list.remove(iterator.next());来移除元素,这个时候的iterator.next()其实已经不是与elem相等的元素了,而是后一个元素了,我们可以写个demo来测试一下 + +```java +ArrayList arrayList = new ArrayList(); +arrayList.add(1); +arrayList.add(2); +arrayList.add(3); +arrayList.add(4); +arrayList.add(5); +arrayList.add(6); +arrayList.add(7); + +Integer elem = 3; +Iterator iterator = arrayList.iterator(); +while (iterator.hasNext()) { + System.out.println(arrayList); + if(iterator.next().equals(elem)) { + arrayList.remove(iterator.next()); + } +} +``` + +输出结果如下: + +```java +[1, 2, 3, 4, 5, 6, 7] +[1, 2, 3, 4, 5, 6, 7] +[1, 2, 3, 4, 5, 6, 7] +[1, 2, 3, 5, 6, 7] +Exception in thread "main" java.util.ConcurrentModificationException + at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) + at java.util.ArrayList$Itr.next(ArrayList.java:851) + at com.test.ArrayListTest1.main(ArrayListTest1.java:29) +``` + +可以看到移除的元素其实不是3,而是3之后的元素,因为调用了两次next()方法,导致游标多移动了。所以应该使用Integer value = iterator.next();将元素取出进行判断。 + +#### 第5种方法 - Iterator遍历,使用Iterator的remove删除元素(结果:正确删除) + +```java +Iterator iterator = arrayList.iterator(); +while (iterator.hasNext()) { + Integer value = iterator.next(); + if (value.equals(3)) {//3是需要删除的元素 + iterator.remove(); + } +} +``` +输出结果: +```java +当前arrayList是[1, 2, 3, 3, 4, 5] +当前arrayList是[1, 2, 3, 3, 4, 5] +当前arrayList是[1, 2, 3, 4, 5] +当前arrayList是[1, 2, 4, 5] +当前arrayList是[1, 2, 4, 5] +当前arrayList是[1, 2, 4, 5] +``` +可以正确删除元素。 + +跟第3种和第4种方法的区别在于是使用iterator.remove();来移除元素,而在remove()方法中会对iterator的expectedModCount变量进行更新,所以在下次循环调用iterator.next()方法时,expectedModCount与modCount相等,不会抛出异常。 + +### ConcurrentModificationException是什么? + +根据ConcurrentModificationException的文档介绍,一些对象不允许并发修改,当这些修改行为被检测到时,就会抛出这个异常。(例如一些集合不允许一个线程一边遍历时,另一个线程去修改这个集合)。 + +一些集合(例如Collection, Vector, ArrayList,LinkedList, HashSet, Hashtable, TreeMap, AbstractList, Serialized Form)的Iterator实现中,如果提供这种并发修改异常检测,那么这些Iterator可以称为是"fail-fast Iterator",意思是快速失败迭代器,就是检测到并发修改时,直接抛出异常,而不是继续执行,等到获取到一些错误值时在抛出异常。 + +异常检测主要是通过modCount和expectedModCount两个变量来实现的, + +- modCount +集合被修改的次数,一般是被集合(ArrayList之类的)持有,每次调用add(),remove()方法会导致modCount+1 + +- expectedModCount +期待的modCount,一般是被Iterator(ArrayList.iterator()方法返回的iterator对象)持有,一般在Iterator初始化时会赋初始值,在调用Iterator的remove()方法时会对expectedModCount进行更新。(可以看看上面的ArrayList.Itr源码) + +然后在Iterator调用next()遍历元素时,会调用checkForComodification()方法比较modCount和expectedModCount,不一致就抛出ConcurrentModificationException。 + +单线程操作Iterator不当时也会抛出ConcurrentModificationException异常。(上面的例子就是) + +#### 总结 +因为ArrayList和HashMap的Iterator都是上面所说的“fail-fast Iterator”,Iterator在获取下一个元素,删除元素时,都会比较expectedModCount和modCount,不一致就会抛出异常。 + +所以当使用Iterator遍历元素(for-each遍历底层实现也是Iterator)时,需要删除元素,一定需要使用 **Iterator的remove()方法** 来删除,而不是直接调用ArrayList或HashMap自身的remove()方法,否则会导致Iterator中的expectedModCount没有及时更新,之后获取下一个元素或者删除元素时,expectedModCount和modCount不一致,然后抛出ConcurrentModificationException异常。 + +### java容器类的层次是怎么样的? + +![1](../static/1.png) + +大致是这样一个图,Collection是一个接口,代表是集合,它有三个子接口,分别是有序集合List,队列Queue,无序集合Set。Map代表键值对。实际上关系会更加复杂一些,以ArrayList为例: + +ArrayList不单是实现了List接口,而且还继承于AbstractList抽象类,同时实现了RandomAccess,Cloneable,Serializable接口。 + +HashMap不单是实现了Map接口,而且继承于AbstractMap抽象类,同时实现了Cloneable,Serializable接口。 + diff --git a/docs/BATInterview.md b/docs/BATInterview.md new file mode 100644 index 0000000..70e0bfa --- /dev/null +++ b/docs/BATInterview.md @@ -0,0 +1,33 @@ +**`大厂面试系列`是我在我的原创技术公众号"大厂面试"里面做的一个专栏,会持续更新,尽可能保证每天发布一篇对面试题的分析文章,公众号首发后才会更新到这里,原创不易,麻烦关注一下我的公众号`大厂面试`,给我一些持续创作的动力,关注可以领取`《大厂面试指北》`的PDF版本,为了方便技术交流,也建了一个技术交流群,欢迎大家扫码加入!谢谢了!** + + + +(如果入群二维码过期了,也可以扫描下面我的微信二维码,加我微信,我拉你进群!) + +![](http://notfound9.github.io/interviewGuide/static/wdsfsdfsmaster.png) + + + + + +## 目录: + +### [【大厂面试01期】高并发场景下,如何保证缓存与数据库一致性?](https://mp.weixin.qq.com/s/hwMpAVZ1_p8gLfPAzA8X9w) +### [【大厂面试02期】Redis过期key是怎么样清理的?](https://mp.weixin.qq.com/s/J_nOPKS17Uax2zGrZsE8ZA) +### [【大厂面试03期】MySQL是怎么解决幻读问题的?](https://mp.weixin.qq.com/s/8D6EmZM3m6RiSk0-N5YCww) +### [【大厂面试04期】讲讲一条MySQL更新语句是怎么执行的?](https://mp.weixin.qq.com/s/pNe1vdTT24oEoJS_zs-5jQ) +### [【大厂面试05期】说一说你对MySQL中锁的理解?](https://mp.weixin.qq.com/s/pTpPE33X-iYULYt8DOPp2w) +### [【大厂面试06期】谈一谈你对Redis持久化的理解?](https://mp.weixin.qq.com/s/nff4fd5TnM-CMWb1hQIT9Q) +### [【大厂面试07期】说一说你对synchronized锁的理解?](https://mp.weixin.qq.com/s/H8Cd2fj82qbdLZKBlo-6Dg) +### [【大厂面试08期】谈一谈你对HashMap的理解?](https://mp.weixin.qq.com/s/b4f5NIPl9uVLkRg_UpWSJQ) + + + + + + + + + + + diff --git a/docs/CodingInterviews.md b/docs/CodingInterviews.md new file mode 100644 index 0000000..bc294f1 --- /dev/null +++ b/docs/CodingInterviews.md @@ -0,0 +1,2905 @@ + +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +下面是主要是自己刷完《剑指Offer》上题后写的题解,有点乱,主要是给自己复习看的,之后有空了会进行整理。 + +#### [题003 二维数组中的查找](#题003) +#### [题004 替换空格](#题004) + +#### [题005从尾到头打印链表](#题005) +#### [题006重建二叉树](#题006) +#### [题007两个栈实现队列](#题007) +#### [题008旋转数组](#题008) + +#### [题009斐波那契数列](#题009) + +#### [题010求某个数的二进制格式下1的个数](#题010) + +#### [题011数值的整数次方](#题011) +#### [题012调整数组顺序使奇数排在前面](#题012) +#### [题013链表的倒数第K个结点](#题013) +#### [题014反转链表](#题014) + +#### [题015 合并链表](#题015) +#### [题016判断一个二叉树是否是另一个二叉树的子结构](#题016) + +#### [题017二叉树的镜像](#题017) +#### [题018顺时针打印矩形](#题018) + +#### [题019包含min函数的栈](#题019) +#### [题020 栈的压入、弹出序列](#题020) + +#### [题021 从上往下打印二叉树](#题021) + +#### [题022 判断是否是二叉搜索树的后序遍历](#题022) + +#### [题023 二叉树中和为某一值的路径](#题023) + +#### [题024 复杂链表的复制](#题024) +#### [题025 二叉搜索树与双向链表](#题025) +#### [题026字符串的排列](#题026) + +#### [题027数组中出现的次数超过一半的数字](#题027) +#### [题028最小的k个数](#题028) +#### [题029连续子数组的最大和](#题029) +#### [题030从1到n中出现的整数中1出现的次数](#题030) + +#### [题031把数组排成最小的数](#题031) + +#### [题032返回第N个丑数](#题032) + +#### [题033 第一个只出现一次的字符](#题033) + +#### [题034 数组中的逆序对](#题034) + +#### [题035 两个的链表的第一个公共节点](#题035) + +#### [题036 数字在排序数组中出现的次数](#题036) + +#### [题037 二叉树的深度](#题037) + +#### [题038 判断是否是平衡二叉树](#题038) +#### [题039 数组中只出现一次的数组](#题039) + +#### [题040 和为S的连续正数序列](#题040) +#### [题041 和为S的两个数字](#题041) + +#### [题042左旋转字符串](#题042) +#### [题043 翻转单词的序列](#题043) +#### [题044 扑克牌顺子](#题044) +#### [题045 圆圈中最后剩下的数字](#题045) +#### [题046 求1+2+…+n](#题046) +#### [题047 不用加减乘除做加法](#题047) + +#### [题048 将字符串转换为整数](#题048) +#### [题049 数组中重复的数字](#题049) + +#### [题050 构建乘积数组](#题050) +#### [题053 字符流中第一个不重复的字符](#题053) +#### [题054 链表中环的入口节点](#题054) +#### [题055 删除链表中重复的节点](#题055) +#### [题056 二叉树的下一个节点](#题056) +#### [题057 对称的二叉树](#题057) +#### [题058 按之字形顺序打印二叉树](#题058) +#### [题059 把二叉树打印成多行](#题059) +#### [题060序列化二叉树](#题060) + +#### [题061 二叉搜索树的第K小的节点](#题061) +#### [题062 数据流的中位数](#题062) + +#### [题063 滑动窗口的最大值](#题063) +#### [题064 矩阵中的路径](#题064) +#### [题065机器人的运动范围](#题065) + +#### [题066剪绳子](#题066) + +### 题003 二维数组中的查找 + +##### 题目内容: + +![image-20201202111714727](../static/image-20201202111714727.png) + +在一个二维[数组](https://cuijiahua.com/blog/tag/数组/)中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维[数组](https://cuijiahua.com/blog/tag/数组/)和一个整数,判断数组中是否含有该整数。 + +如果在一个二维数组中找到数字7,则返回true,如果没有找到,则返回false。 + +##### 思路 + +就是从右上角开始遍历,假设要查找的数为A,当前遍历的数为B,B的特点是B所在行里面最大的数,也是B所在列最小的数,如果遍历的数BA,那么B所在的列可以排除(比B都大)。 + +##### 代码: + +```java + static boolean find(int target, int [][] array) { + int rowLength = array.length;//总行数 + int colLength = array[0].length;//总列数 + + int currentRow = 0;//起始遍历位置是右上角,行号为0 + int currentCol = colLength - 1;//起始遍历位置是右上角,列号为最大值 + while (currentRow = 0) {//防止超出边界 + if (array[currentRow][currentCol] == target) { + return true; + } else if (array[currentRow][currentCol] > target) {//比要找的数大,那么排除更大的数,也就是排除这一列 + currentCol--; + } else {//比要找的数小,那么排除更小的数,也就是排除这一行 + currentRow++; + } + } + return false; +} +``` + +##### 总结 + +注意这个currentRow = 0判断条件,防止越界。 + +### 题004 替换空格 + +请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为 We%20Are%20Happy。 + +最暴力的方法就是从头开始对字符数组进行遍历,碰到空格,就进行替换,然后让后面的元素向后移动2位,但是这样每个空格都需要让后面的元素移动,复杂度会是O(n^2)。 + +优化的方法是先遍历一编字符串,知道字符串的空格数,然后计算得到替换后的长度=原来长度+2*空格数,然后从字符串的末尾进行遍历,每次把元素移动到计算后的数组下标的位置,并且对空格进行替换。 + +```java + String replaceSpace(StringBuffer str) { + int number = 0;//空格数目 + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) == ' ') { + number++; + } + } + int oldLength = str.length(); + int newLength = str.length()+ 2*number; + + str.setLength(newLength); + int tempIndex = newLength - 1;//新字符串正在的位置 + for (int i = oldLength-1; i >=0; i--) {//遍历 + if (str.charAt(i) != ' ') { + str.setCharAt(tempIndex, str.charAt(i)); + tempIndex--; + } else { + str.setCharAt(tempIndex, '0'); + str.setCharAt(tempIndex-1, '2'); + str.setCharAt(tempIndex-2, '%'); + tempIndex = tempIndex - 3; + + } + } + return str.toString(); +} +``` + +## 题005 从尾到头打印链表 + +输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。 + +总结: + +首先通过开始的判断,来排除链表为空的情况,直接返回空数组,链表不为空,取下一个节点,判断下一个节点是否为空, + +- 不为空,那么递归调用printListFromTailToHead方法来获取后面的节点反序生成的ArrayList,然后添加当前的节点的值,然后返回arrayList。 +- 为空,那么说明当前节点是链表尾部节点,直接创建一个ArrayList,然后添加当前节点的值,然后返回arrayList。 + +其实原理就是先递归遍历,然后再打印,这样链表打印的顺序就是逆序的了。 +```java +ArrayList list = new ArrayList(); +public ArrayList printListFromTailToHead(ListNode listNode) { + if(listNode == null ){ + return list; + } + printListFromTailToHead(listNode.next); + list.add(listNode.val); + return list; +} +``` + + + +## 题006重建二叉树 + +输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。 + +![image-20200624204953477](../static/image-20200624204953477.png) + +前序遍历结果和中序遍历结果: + +![image-20200624204944577](../static/image-20200624204944577.png) + +前序遍历结果分布是二叉树根节点,左子树,右子树。 + +中序遍历结果分布是左子树,二叉树根节点,右子树。 + +所以根据前序遍历结果的第一个元素获取到根节点,然后根据根节点把中序遍历结果分为两半,得到左子树的中序遍历结果,然后根据左子树的长度可以去前序遍历结果中分离出左子树的前序遍历结果,右子树也是如此,所以可以递归得构造出整个二叉树。 + +```java + public static TreeNode reConstructBinaryTree(int[] pre, int[] in) { + return reConstructBinaryTree(pre, 0, pre.length-1, in, 0, in.length-1); + } + + public static TreeNode reConstructBinaryTree(int[] pre, int preStart, int preEnd, int[] in, int inStart, int inEnd) { + if (preStart > preEnd || inStart > inEnd) { + return null; + } + TreeNode treeNode = new TreeNode(pre[preStart]); + for (int i = inStart; i <= inEnd; i++) { + if (in[i] == pre[preStart]) { + int leftLength = i - inStart;//左子树长度 + treeNode.left = reConstructBinaryTree(pre, preStart + 1, preStart+leftLength, in, inStart, i-1); + treeNode.right = reConstructBinaryTree(pre, preStart +leftLength+1, preEnd, in, i+1, inEnd); + break; + } + } + return treeNode; + } +``` + +## 题007两个栈实现队列 + +用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。 队列中的元素为int类型。 + +思路:因为队列是先进先出,栈是先进后出,一个栈用来进元素,一个栈用来反序,就可以实现队列。用一个栈stack1来进元素,另一个栈stack2来出元素,stack2为空时,将stack1的元素依次出栈,然后依次入栈到stack2,然后从stack2取出栈顶元素。 + +```java + Stack stack1 = new Stack(); + Stack stack2 = new Stack(); + public void push(Integer number) { + stack1.push(number); + } + public Integer pop() { + if (stack2.size()>0) { + return stack2.pop(); + }else if (stack1.size()>=0) { + while (stack1.size()>0) { + Integer temp = stack1.pop(); + stack2.push(temp); + } + return stack2.pop(); + } + return null; + } +``` + +## 题008旋转数组 + +把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。 + +```java +int minNumberInRotateArray(int[] array) { + if (array[0]= array[end]) { + System.out.println(start+"======"+mid+"====="+end); + if (end-start == 1) { + return array[end]; + } + mid = (end + start)/2; + + if (array[start] == array[mid] && array[start] == array[end]) {//左边界,中间值,右边界相等 + int min = array[start]; + for (int i = start+1; i <=end ; i++) { + if (array[i]< min) { + min = array[i]; + } + } + return min; + } + if ( array[mid]>=array[start]){//左半部分是递增的,那么就去掉左半部分 + start = mid; + } else if(array[mid]<=array[end]) {//右半部分是递增的,那么就去掉右半部分 + end = mid; + } + } + return array[mid]; + } +``` + +思路:旋转数组其实就是一个递增的数组,整体移动了一下元素,类似3,4,5,1,2这种。要查找最小的元素,可以遍历一遍数组,复杂度为O(N),这样就太暴力了,因为这个旋转数组其实是有规律的,可以根据左边界,右边界,中间值来判断最小值的位置 + +* 左边界<=中间值 说明左边界到中间值这一段是递增的,也就是最小值不处于这一段。这样可以排除掉这一段,然后去另一段里面遍历查找。 + +* 中间值<=右边界 说明中间值到右边界这一段是递增的,也就是最小值不处于这一段。这样可以排除掉这一段,然后去另一段里面查找。 + + 一直排除到最后,右边界下标-左边界下标==1时,说明左边界是最大值,右边界是最小值,此时整个循环结束。 + +* 特殊情况 左边界== 中间值==右边界 说明无法判断最小值位于哪里,只能从左边界到右边界进行遍历然后获得最小值。 + +## 题009斐波那契数列 + +大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39 + +##### 递归解法 + +f(n) =f(n-1)+f(n-2) + +n为0时,f(n)为0 + +n为1时,f(n)为1 + +```java +int Fibonacci(int n) { + if (n == 0){ + return 0; + } else if (n==1) { + return 1; + } + return Fibonacci(n-1) + Fibonacci(n-2); +} +``` + + +## 题010求某个数的二进制格式下1的个数 + +输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 + +第一种解法就是每次拿二进制数最低位与1取&结果,如果结果为1,代表最低位为1,count+1,然后将二进制数>>1位,让倒数第二位成为最低位然后比较。(负数右移动,左边的空位是会补1的,所以如果是负数A右移动,会先变成变成A/2,最终变为-1,-1的二进制数继续右移还是-1) + +在计算机中,数值都是使用补码进行表示的,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。正数的补码就是原码,负数的补码就是绝对值取原码,取反得到反码,然后再+1 + +``` +-1的补码是 +1、先取1的原码:00000000 00000000 00000000 00000001 +2、得反码: 11111111 11111111 11111111 11111110 +3、得补码: 11111111 11111111 11111111 11111111 + +-2的补码是 +1、先取1的原码:00000000 00000000 00000000 00000010 +2、得反码: 11111111 11111111 11111111 11111101 +3、得补码: 11111111 11111111 11111111 11111110 +``` + +这种解法的问题在于负数的最高位是1,向右移动一位后,为了保证移位后还是一个负数,最高位还是设置为1,这样就会陷入死循环(-1右移动还-1)。 + +第二种解法就是不拿最低位去进行比较了,而是定义一个变量flag=1,拿二进制数与flag进行比较,判断最低位是否为1,为1那么count+1,然后将flag<<1位,拿二进制数与flag进行比较,判断倒数第二位是否为1,然后一直把每一位都判断完,但是在Java中,int是4字节,32位,这样需要判断32次。 + +```java +public int NumberOf1(int n) { + int count = 0; + int bit = 1; + int times =0; + while(times<32) { + //不为0说明这一个二进制位为1, + if((bit&n) != 0) {count++;} + times++; + bit = bit<<1; + } + return count; +} +``` + +第三种解法可以做到二进制数有多少个1就判断多少次。具体原理是 + +n&(n-1)的结果其实是将n的最右边的1去掉,所以多次执行n&(n-1)直到将所有的1都去掉,以此来计数。 + +```java +public int NumberOf1(int n) { + int count = 0; + while (n != 0) { + count++; + n = n & (n-1); + } + return count; + } +``` + +## 题011数值的整数次方 + +给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent 。求 base 的 exponent 次方。 + +思路可以用一个循环,让底数直接相乘,循环次数为整数次方数,这样如果有n次方,复杂度为O(N),可以 + +假设f(n)代表x的n次方 + +n为偶数时, + +f(n)= f(n/2)*f(n/2) + +n为奇数时, + +f(n)= f(n/2)*f(n/2) * x + +```java +double Power(double base ,int exponent) { + if (exponent<0) { + if (base!=0) { + return 1/Power(base,-exponent); + } else { + //抛出异常 + } + } + if (exponent == 0) { return 1;} + else if (exponent == 1) { return base;} + double result = Power(base, exponent / 2); + if ((exponent & 0x1) == 0) { + return result * result; + } + return base * result * result; +} +``` + +## 题012调整数组顺序使奇数排在前面 + +输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 + +解题思路: + +如果可以使用额外的内存空间,可以对数组遍历两遍,一遍将奇数取出,存放在额外的数组中去,一遍把剩下的偶数存放到额外的数组中去。 + +如果不能使用额外的内存空间,就是查找奇数,然后与前面的元素互换,一直到替换到最后一个奇数的后面,有点像是冒泡排序。(因为不能改变相对位置,所以不能用快排) + +冒泡排序是其实是交换,从头开始,依次判断两个相邻的元素,将更大的元素向右交换,遍历一次后可以将当前序列最大的元素交换到最后面去,下次遍历就不用管最后一个元素。 + +```java +public static void reOrderArray(int [] array) { + int j = 0;//第一个偶数的位置。用于存放下一个奇数 + for (int i = 0; i < array.length; i++) { + if (array[i]%2==1) {//奇数 + for (int k = i;k>j;k--) { + int temp = array[k]; + array[k] = array[k-1]; + array[k-1] = temp; + } + j++; + } + } +} +``` + +## 题013链表的倒数第K个结点 + +输入一个链表,输出该链表中倒数第k个结点。 + +一个指针A先向前走k-1步,然后一个指针B指向头结点,A,B同时往后面走,直到A成为最后一个节点。 + +```java +ListNode FindKthToTail(ListNode head, int k) { + if (head==null || k <= 0) {//空链表,或者k小于等于0 + return null; + } + ListNode secondNode = head; + for (int i=0 ; i < k-1 ; i++) {//向前走k-1步 + if (secondNode.next==null) {//链表长度不足k个 + return null; + } + secondNode = secondNode.next; + } + ListNode firstNode = head; + while (secondNode.next != null) {//一直遍历到secondNode成为最后一个节点 + secondNode = secondNode.next; + firstNode = firstNode.next; + } + return firstNode; +} +``` + + + +## 题014反转链表 + +输入一个链表,反转链表后,输出新链表的表头。 + +A = head + +B = head.next + +head = null;//特别注意需要将原本的头结点置为null,否则原来的头结点的next会引用原来的第二个节点,形成一个环。 + +上一个节点A,当前节点B,下一个节点C,让 + +C = B.next; + +B.next = A; + +A = B; + +B = C; + +一直到B为null,此时A为最后一个节点. + +```java +public static ListNode ReverseList(ListNode head) { + + if (head == null) return null;//链表为空 + if (head.next==null) return head;//链表只有一个节点 + ListNode lastNode = head; + ListNode currentNode = head.next; + head.next = null;//将原来的头结点指向null + + while (currentNode != null) {//一直到currentNode是最好一个节点 + ListNode saveNextNode = currentNode.next; + currentNode.next = lastNode; + lastNode = currentNode; + currentNode = saveNextNode; + } + return lastNode; +} +``` + +这种解法好理解一点,就是使用first,second,three保存三个连续的节点,依次后移动 + +```java +public ListNode findLastNode(ListNode node) { + if(node==null ||node.next ==null) { + return node; + } + //至少有两个节点 + ListNode first = node; + ListNode second = node.next; + ListNode three = second.next; + first.next = null; + while(second!=null) { + second.next = first; + first = second; + second = three; + if (three == null){break}; + else { + three = three.next; + } + } + return first; +} +``` + + + +## 题015 合并链表 + +输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。 + +遍历写法: + +```java + ListNode Merge(ListNode list1,ListNode list2) { + if (list1 == null || list2 == null ) { //存在链表为空 + return list1 == null ? list2 : list1; + } + ListNode newHead, currentNode; + //取出较小值作为新链表的头结点 + if (list1.val < list2.val) { + newHead = list1; + list1 = list1.next; + }else { + newHead = list2; + list2 = list2.next; + } + currentNode = newHead; + while (list1 != null && list2 != null ) { + if (list1.val printMatrix(int [][] matrix) { + if(matrix==null) { + return null; + } + ArrayList arrayList = new ArrayList(); + return printMatrix(arrayList, matrix, 0, matrix.length-1,0,matrix[0].length -1); + } + + public ArrayList printMatrix(ArrayList arrayList, int [][] matrix,int rowStart, int rowEnd, int colStart, int colEnd) { + if (rowStart> rowEnd || colStart>colEnd) { + return arrayList; + } + for (int i = colStart; i <=colEnd;i++) { + arrayList.add(matrix[rowStart][i]); + } + for (int i = rowStart+1; i <=rowEnd-1;i++) { + arrayList.add(matrix[i][colEnd]); + } + for (int i = colEnd; i >=colStart&&rowEnd>rowStart;i--) {//要加rowEnd>rowStart判断,不然对于单行情况会重复打印 + arrayList.add(matrix[rowEnd][i]); + } + for (int i = rowEnd-1; i >=rowStart+1&& colStart < colEnd;i--) {//要加rowEnd>rowStart判断,不然对于单列情况会重复打印 + arrayList.add(matrix[i][colStart]); + } + printMatrix(arrayList, matrix,rowStart+1,rowEnd-1, colStart+1,colEnd-1); + return arrayList; + } +``` + +## 题019 包含min函数的栈 + +定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数(时间复杂度应为O(1))。 + +暴力解法极速 + +就是用另外一个栈来存栈每个元素的对应的栈最小值,其实很多原生的元素对应的栈最小值都是一样的,只有栈最小值自己出栈或者入栈后才会有区别。 + +所以入栈时,如果元素<=之前的最小值,那么对元素入min栈, + +出栈时,如果元素==min栈的栈顶元素,也就是当前的最小值,那么需要同时对min栈做出栈操作。 + +```java +private Stack stack = new Stack(); +private Stack minStack = new Stack(); +public void push(int node) { + stack.push(node); + if (minStack.size()==0 || node<=minStack.peek()) { + minStack.push(node); + } +} +public void pop() { + if(stack.size()>0 && minStack.size()>0) { + Integer value = stack.pop(); + if (value == minStack.peek()) { + minStack.pop(); + } + } +} + +public int top() { + return stack.peek(); +} + +public int min() { + return minStack.peek(); +} +``` + +## 题020 栈的压入、弹出序列 + +输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的) + +一个是栈的压入顺序A,一个是出栈顺序B,判断出栈顺序是否是这个栈的出栈顺序。 + +例如压入顺序是1,2,3,4,5, + +出栈顺序4,5,3,2,1可能是该栈的弹出顺序, + +第一种解法,模拟压入,弹出法。 + +就出栈要么是在压入过程中,就是压入一个元素时,然后进行出栈,或者全部压完之后再出栈。 + +所以可以对压入顺序A进行遍历,判断A压入的元素是否是出栈顺序B最前面的元素, + +* 如果不是,那么说明只是把元素压入栈tempStack,现在还没有出栈 +* 如果是,那么现在元素可以出栈了,将元素先压入tempStack,然后对B继续向后遍历,继续之前的循环。 + +循环结束后,继续对B继续向后遍历,并且与tempStack的栈顶元素进行判断,是的话就出栈,知道tempStack的元素与B中遍历到的元素不相等,那么说明B与A对应不上。 + +```java +public static boolean IsPopOrder1(int [] pushA,int [] popB) { + if (pushA==null||popB==null) { + return false; + } + Stack stack = new Stack<>(); + int j = 0; + //先根据入栈序列,往栈中压入数据 + for (int i = 0; i < pushA.length; i++) { + //如果当前栈顶元素跟出栈序列当前遍历的元素一样,那么进行出栈处理 + while (stack.size()>0 && j0 && j PrintFromTopToBottom(TreeNode root) { + ArrayList arrayList = new ArrayList(); + if (root==null) { + return arrayList; + } + ArrayList queue = new ArrayList(); + queue.add(root); + while (queue.size()>0) { + TreeNode treeNode = queue.remove(0); + arrayList.add(treeNode.val); + if (treeNode.left!=null) queue.add(treeNode.left); + if (treeNode.right!=null) queue.add(treeNode.right); + } + return arrayList; +} +``` + +也可以通过队列来实现,将根节点添加到队列中,然后对队列进行循环,每次从队列取出一个元素,添加到ArrayList中去,然后将左,右子节点添加到队列中去,然后继续循环,一直到队列中取不到元素。(Java中队列的实现Queue,add(),remove()) + +##### 深度优先遍历 + +一般是使用栈来实现,一开始将 + +1.根节点加入栈, + +2.将栈顶元素出栈,打印这个节点,然后将它的右子节点入栈,将其左节点入栈 + +3.重复2操作,一直到栈中元素为空。 + +也可以使用递归实现,深度遍历递归实现 + +```java +ArrayList list = new ArrayList(); +void deepTranverse(TreeNode node) { + if(node!=null) { + list.add(node); + deepTranverse(node.left); + deepTranverse(node.right); + } +} +//栈的解法 +void deepTranverse(TreeNode node) { + Stack stack=new Stack(); + List list=new ArrayList(); + if(root==null) + return list; + //压入根节点 + stack.push(root); + //然后就循环取出和压入节点,直到栈为空,结束循环 + while (!stack.isEmpty()){ + TreeNode t=stack.pop(); + if(t.right!=null) + stack.push(t.right); + if(t.left!=null) + stack.push(t.left); + list.add(t.val); + } + return list; +} +``` + + + +## 题022 判断是否是二叉搜索树的后序遍历 + +输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出 Yes ,否则输出 No 。假设输入的数组的任意两个数字都互不相同。 + +二叉搜索树的特点在于,左子树所有节点<根节点<右子树所有节点,后续遍历的遍历顺序是左子树,右子树,根节点,所以取出数组最后一个元素,也就是根节点,然后遍历序列,发现第一个比根节点大的数之后,这个是也就是临界点,后面的数肯定也需要比根节点大,否则不是后续遍历,遍历完成后。 + +* 没有临界点(也就是第一个右子树的节点),全部都是左子树,递归调用判断, +* 临界点等于第一个元素,全部都是右子树,递归调用判断, +* 其他情况,将序列分为左子树,右子树,递归调用判断, + +(遍历时如果只有一个元素,那么直接返回正确,肯定满足要求) + +```java +public static boolean VerifySquenceOfBST(int [] sequence) { + if (sequence == null || sequence.length ==0) { + return false; + } + return VerifySquenceOfBST(sequence,0,sequence.length-1); +} + +public static boolean VerifySquenceOfBST(int[] sequence, int start, int end) { + if (start==end) { + return true; + } + Integer rightChildIndex = null; + for (int i = start;i sequence[end]&&rightChildIndex==null) { + rightChildIndex = i; + } + if (rightChildIndex!=null&&sequence[i] < sequence[end]) {//右子树有更小的元素 + return false; + } + } + if(rightChildIndex==null) {//说明全部位于左子树 + return VerifySquenceOfBST(sequence,start,end-1); + } else if(rightChildIndex== start) {//说明全部位于右边子树 + return VerifySquenceOfBST(sequence,start,end-1); + } + //继续校验 + return VerifySquenceOfBST(sequence,start,rightChildIndex-1) && VerifySquenceOfBST(sequence,rightChildIndex, end-1); +} +``` + +## 题023 二叉树中和为某一值的路径 + +输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的 list 中,数组长度大的数组靠前。) + +就是递归调用每个节点的左右子树,然后将节点值相加,如果节点值和为某个预期值,并且该节点为叶子节点,那么这条路径就是要找的路径。 + +```java +public ArrayList> FindPath(TreeNode root, int target) { + ArrayList> arrayContainer = new ArrayList>(); + if (root == null) { + return arrayContainer; + } + judgeIfIsTarget(arrayContainer, new ArrayList(),root,target,0); + return arrayContainer; +} + +public void judgeIfIsTarget(ArrayList> arrayContainer, ArrayList currentArrayList, TreeNode root, int target,int sum) { + if (root == null) { + return ; + } + currentArrayList.add(root.val); + sum = sum+root.val; + if (sum == target && root.left == null && root.right == null) { + ArrayList copyArrayList = new ArrayList<>(currentArrayList); + arrayContainer.add(copyArrayList); + } else { + judgeIfIsTarget(arrayContainer, new ArrayList<>(currentArrayList), root.left, target,sum); + judgeIfIsTarget(arrayContainer, new ArrayList<>(currentArrayList), root.right, target,sum); + } +} +``` + +## 题024 复杂链表的复制 + +输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head 。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空) + +第一种解法就是需要额外的空间,一个哈希表来辅助,哈希表存储了旧链表的节点与新节点的映射关系,把链表当成普通单链表来进行复制,复制完之后通过去哈希表中查找节点来对特殊指针进行赋值。 + +第二种解法就是不需要额外的空间,但是将新节点先放在旧链表中对应的节点的后面,然后当成单链表复制完毕后,复制完之后,去对特殊指针赋值,然后再将新旧链表分离。 + +```java +public RandomListNode Clone(RandomListNode pHead) + { + if (pHead == null){return null;} + + RandomListNode currentNode= pHead; + //当成单链表来赋值一遍 + while (currentNode!=null) { + RandomListNode newNode = new RandomListNode(currentNode.label); + + RandomListNode nextNode = currentNode.next; + currentNode.next = newNode; + newNode.next = nextNode; + currentNode = nextNode; + + + } + //设置新链表的特殊指针 + RandomListNode oldCurrentNode= pHead; + RandomListNode newCurrentNode; + while (oldCurrentNode!=null) { + newCurrentNode = oldCurrentNode.next; + RandomListNode specialNode = oldCurrentNode.random; + newCurrentNode.random = specialNode == null ? null : specialNode.next;//random可能会为null + oldCurrentNode = oldCurrentNode.next.next; + + } + + //链表分离 + oldCurrentNode = pHead; + newCurrentNode = oldCurrentNode.next; + + RandomListNode newHead = pHead.next; + + while (oldCurrentNode!=null) { + + RandomListNode oldNextNode = oldCurrentNode.next.next; + if (oldNextNode== null) {//到最后一个节点了 + oldCurrentNode.next = null;//不然会指向新链表的最后一个节点 + break; + } + RandomListNode newNextNode = oldNextNode.next; + oldCurrentNode.next = oldNextNode; + oldCurrentNode = oldNextNode; + newCurrentNode.next = newNextNode; + newCurrentNode = oldCurrentNode.next; + } + return newHead; + } +``` + + + +## 题025 二叉搜索树与双向链表 + +输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 + +二叉搜索树的中序遍历的结果就是递增的序列,所以递归实现二叉搜索树的中序遍历 + +```java + TreeNode head = null;//主要记录双向链表头结点 + TreeNode lastNode = null;//主要记录中序遍历时,上一次遍历的节点 + public TreeNode Convert(TreeNode pRootOfTree) { + if (pRootOfTree == null) { + return null; + } + Convert(pRootOfTree.left); + if (head == null) {//这里相当于是把第一次执行Convert方法的元素设置为链表头结点,也就是中序遍历第一个位置的节点,也就最左边的叶子节点。 + head = pRootOfTree; + } + if (lastNode != null) {//中序遍历时,假设存在上一个遍历的节点,将上一个节点与这个节点进行关联 + lastNode.right = pRootOfTree; + pRootOfTree.left = lastNode; + } + //完成对当前节点的遍历,将当前设置为lastNode。 + lastNode = pRootOfTree; + Convert(pRootOfTree.right); + return head; + } +``` + +## 题026字符串的排列 + +输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 + +输入描述:输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。 + +##### 回溯算法 + +对于一个字符串的序列,可以看成第一个字符+剩下字符的排列,每次从后面序列中取一个不重复的字符,与第一个字符交换,然后递归地对后面的字符调用函数进行排列。 + +1.遍历字符串,每次取一个不同的字符作为首字符,然后对后面剩余的字符串递归调用方法,对后面的字符串进行排列。 + +2.在递归的尽头,也就是当前子字符串只有1个元素,会将所有元素添加到数组。 + +```java + public ArrayList Permutation1(String str) { + ArrayList list = new ArrayList(); + if (str==null||str.length()==0) { + return list; + } + Permutation(list,str,0,str.length()-1); + return list; + } + public void Permutation(ArrayList list,String str,int start,int end) { + StringBuffer stringBuffer = new StringBuffer(str); + HashSet set = new HashSet<>(); + //递归到最后一层,此时只有一个字符了,肯定是排在第一个,将当前字符串添加到list + if (start == end) { + list.add(str); + return; + } + for (int i = start; i <= end; i++) { + Character c = str.charAt(i); + if (set.contains(c) == false) { + //添加当前字符到set,代表已经做了首元素了 + set.add(c); + //将当前字符与首字符交换 + Character startChar = stringBuffer.charAt(start); + stringBuffer.setCharAt(start,c); + stringBuffer.setCharAt(i,startChar); + //继续递归 + Permutation(list, stringBuffer.toString(),start+1,end); + } + } + } +``` + +## 题027数组中出现的次数超过一半的数字 + +数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出 2 。如果不存在则输出 0 。 + +就是一个数组,假设包含一个超过次数一半的元素,那么去除掉两个不相等的元素后,剩下的数组中,这个元素还是会出现次数超过一半。 + +(原理就是每次排除两个不相等的元素,最后剩下的一个元素,或者两个元素一定是次数超过一半的这个数字。) + +```java +public int MoreThanHalfNum_Solution(int [] array) { + if (array==null||array.length==0) { + return 0; + } + if (array.length==1) { + return array[0]; + } + int result = array[0]; + int times = 1; + for (int i = 1; i < array.length; i++) { + if (times == 0) { + times = 1; + result = array[i]; + } else if (array[i] == result) { + times++; + } else { + times--; + } + } + //下面就是判断这个数字是否满足条件 + int statTimes = 0; + for (int i = 0; i < array.length; i++) { + if (array[i] == result) { + statTimes++; + } + } + if (statTimes>array.length/2) { + return result; + } + return 0; +} +``` + +## 题028最小的k个数 + +输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。 + +其实就是插入排序,只不过只是对k个数进行插入排序,但是这样的时间复杂度会是O(N^2),如果是使用快排来做,平均时间复杂度就是O(N)。 + +```java +public ArrayList GetLeastNumbers_Solution(int [] input, int k) { + ArrayList arrayList = new ArrayList(); + if(input==null || input.length==0 ||input.length arrayList.get(arrayList.size()-1)) {//子数组个数达到了K,并且当前数比子数组最后一个数大 + continue; + } else if (input[i] < arrayList.get(arrayList.size()-1)) { + arrayList.remove(arrayList.size()-1); + arrayList.add(input[i]); + } + + //将最后一个元素移动合适的位置 + for (int j = arrayList.size()-1; j > 0 ; j--) { + if (arrayList.get(j) < arrayList.get(j-1)) { + int temp = arrayList.get(j); + arrayList.set(j, arrayList.get(j-1)); + arrayList.set(j-1, temp); + } + } + + } + return arrayList; + } +``` + +## 题029连续子数组的最大和 + +例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第1个开始,到第4个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1) + +使用动态规划的方法来进行思考 + +f(n) 有两种取值 + +* 当f(n-1)<=0时,取array[n],从这个元素重新开始 + +* 当f(n-1)>0时,取f(n-1)+array[n] + + +```java + public int FindGreatestSumOfSubArray(int[] array) { + if(array==null || array.length==0) { + return 0; + } + int currentSum = array[0]; + int maxSum = currentSum; + for (int i = 1; i < array.length; i++) { + if (currentSum<0) {//前面的和是负数,就直接丢弃 + currentSum = array[i]; + } else { + currentSum = currentSum + array[i]; + } + if (currentSum>maxSum) { + maxSum = currentSum; + } + } + return maxSum; + } +``` + +## 题030从1到n中出现的整数中1出现的次数 + +求出1~13的整数中 1 出现的次数,并算出 100~1300 的整数中1出现的次数?为此他特别数了一下 1~13 中包含1的数字有 1、10、11、12、13 因此共出现 6 次,但是对于后面问题他就没辙了。ACMer 希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。 + +解法一,就是对1到n进行遍历,对每个数统计该数1出现的次数,统计时用这个数x%10,判断个位数是否为1,然后用x=x/10的结果继续%10来进行判断个位数为1,一直到x=0,统计到x包含1的个数,这样的话,一共有N个数,每个数计算的时间复杂度log10 N,总时间复杂度是N*log10 (N)也就是Nlog(N) + +解法二:还是对1到n进行遍历,对每个数统计该数1出现的次数,将每个数转换为字符串,判断字符串包含字符"1"的个数,但是将数字转换为字符串的这个过程,由于使用了StringBuffer的append()方法,然后使用了Integer的getChars方法,复杂度还是Log10 (N),所以总复杂度还是Nlog(N) + +```java +public int NumberOf1Between1AndN_Solution(int n) { + int count = 0; + for (int i = 1; i <= n; i *= 10) { + int a = n / i,b = n % i; + //之所以补8,是因为当百位为0,则a/10==(a+8)/10, + //当百位>=2,补8会产生进位位,效果等同于(a/10+1) + count += (a + 8) / 10 * i + ((a % 10 == 1) ? b + 1 : 0); + } + return count; +} +``` + +## 题031把数组排成最小的数 + +输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。 + + +就是把小的数排前面,这样排出来的数就是最小的, + +思路就是冒泡排序,将小数往后面挪动,只是判断数A与数B之间的大小,是以AB和BA的大小来决定的,所以对A和B进行拼接成,AB和BA,通过字符串比较,判断AB和BA的大小,AB>BA,说明A排起来会比较大,往后面挪。 + +```java +public String PrintMinNumber(int [] numbers) { + if(numbers.length==1) { + return new StringBuffer().append(numbers[0]).toString(); + } + StringBuffer stringBuffer = new StringBuffer(); + for (int i = 1; i < numbers.length; i++) { + for (int j = 0; j < numbers.length - i ; j++) { + if (compare(numbers[j], numbers[j+1])) {//numbers[i-1]更大 + int temp = numbers[j]; + numbers[j] = numbers[j+1]; + numbers[j+1] = temp; + } + } + } + for (int i = 0; i < numbers.length; i++) { + stringBuffer.append(numbers[i]); + } + return stringBuffer.toString(); +} + +public Boolean compare(int a, int b) { + String first = new StringBuffer().append(a).append(b).toString(); + String second = new StringBuffer().append(b).append(a).toString(); + for (int i = 0; i < first.length(); i++) { + Character char1 = first.charAt(i); + Character char2 = second.charAt(i); + if (char1.equals(char2)) { + continue; + } else if (char1 > char2) { + return true; + } else { + return false; + } + } + return true; +} +``` + +## 题032返回第N个丑数 + +把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。 + +1 2 3 5 + +第一个丑数是1,除了1以外,其他丑数都是2,3,5之间相乘得到的,也就是丑数的因子都是2,3,4,5,6, + +第一种解决方案就是从1开始对所有整数遍历,将每个数一直除以2,3,5,看能否除尽,能除尽代表是丑数,一直得到第N个丑数。 + +第二种解决方案就是用一个数组来存丑数,将2,3,5当前对应的最小丑数乘以2,3,5,取最小值,作为最新的丑数,一直计算到第N个丑数。 + +```java +public int GetUglyNumber_Solution(int index) { + if (index == 0) return 0; + int[] array = new int[index]; + array[0] = 1;//最小的丑数是1 + int index2 =0 ,index3 = 0, index5 = 0;//分别代表上一次乘了2,3,5的index + for (int i = 1; i< index;i++){ + int temp2 = array[index2]*2; + int temp3 = array[index3]*3; + int temp5 = array[index5]*5; + int minTemp = temp2 < temp3 ? temp2 : temp3; + minTemp = minTemp < temp5 ? minTemp : temp5; + if (temp2 == minTemp) { + index2++; + } + if (temp3 == minTemp) { + //可能存在一个丑数可以由多种丑数相乘得到, + // 例如12可以是6*2,也可以是4*3,所以这里的三个if需要分开判断赋值 + index3++; + } + if (temp5 == minTemp) { + index5++; + } + array[i] = minTemp; + } + return array[index-1]; +} +``` + +## 题033 第一个只出现一次的字符 + +在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写). + +因为java中的字符char类型是 2字节的,也就是16位,所以只有256*256种可能,所以可以使用一个数组来对字符出现的次数进行记录。第一遍扫描字符串,统计字符出现次数,第二遍扫码字符串,返回只出现一次的字符的下标。 + +```java + public int FirstNotRepeatingChar(String str) { + if (str == null|| str.length() == 0) { + return -1; + } + int[] array = new int[256*256]; + for (int i = 0; i < str.length(); i++) { + char value = str.charAt(i); + array[value]++; + } + for (int i = 0; i < str.length(); i++) { + char value = str.charAt(i); + if(array[value] == 1) { + return i; + } + } + return -1; + } +``` + +## 题034 数组中的逆序对 + +在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 + +输入描述: 题目保证输入的数组中没有的相同的数字 + +数据范围: + + 对于%50的数据,size<=10^4 + + 对于%75的数据,size<=10^5 + + 对于%100的数据,size<=2*10^5 + +逆序对就是前面的数比后面的数大,就是一个逆序对,可以使用归并排序,每次组合并时,右边的组的数比左边的数小时,会出现逆序对,逆序对的个数为当前左边的组的元素个数。 + +```java +public int InversePairs(int [] array) { + int[] temp = new int[array.length]; + int count = inversePairsort2(array,0,array.length-1,temp); + return count; +} + +public int inversePairsort2(int[] array, int left, int right, int[] temp) { + int count = 0; + if (left < right) { + int mid = (left + right)/2; + count += inversePairsort2(array,left,mid, temp); + count += inversePairsort2(array,mid+1, right ,temp); + //进行合并 + int leftIndex=left,rightIndex = mid+1,newIndex = left; + while (leftIndex<=mid && rightIndex<= right) { + if (array[leftIndex] <= array[rightIndex]) { + temp[newIndex] = array[leftIndex]; + leftIndex++; + newIndex++; + } else { + temp[newIndex] = array[rightIndex]; + count += mid - leftIndex + 1; + if (count>1000000007) { + System.out.println(count); + } + rightIndex++; + newIndex++; + } + } + while (leftIndex<=mid) { + temp[newIndex] = array[leftIndex]; + leftIndex++; + newIndex++; + } + while (rightIndex<=right) { + temp[newIndex] = array[rightIndex]; + rightIndex++; + newIndex++; + } + + for (int i = left; i <= right; i++) { + array[i] = temp[i]; + } + } + return count; +} +``` + +思路就是先递归对数组进行分组,一直到每个组只有一个元素,然后每个组按大小进行合并,形成一个新的组。每次合并的时间复杂度是N,大概需要合并log(N)次,所以总时间复杂度是 Nlog(N),这样写简单是简单,就是空间复杂度太高了,每次创建新数组,空间复杂度是Nlog(N) + +```java + public static int[] sort(int[] array,int start, int end) { + if (start == end) { + return new int[]{array[start]}; + } + + int mid = (start+end)/2; + int[] leftArray = sort(array, start, mid); + int[] rightArray = sort(array, mid+1, end); + int leftIndex = 0, rightIndex = 0, newIndex = 0; + //开始合并 + int[] newArray = new int[leftArray.length+rightArray.length]; + while (leftIndexlength2) { + length1--; + currentNode1 = currentNode1.next; + } else { + length2--; + currentNode2 = currentNode2.next; + } + } + while (currentNode1!=null&¤tNode2!=null) { + if (currentNode1==currentNode2){ + return currentNode1; + } else { + currentNode1 = currentNode1.next; + currentNode2 = currentNode2.next; + } + } + return null; +} +``` + +## 题036 数字在排序数组中出现的次数 + +统计一个数字在排序数组中出现的次数。 + +正常的二分查找是这样的,找不到时就会返回-1 + +```java +public int findByHalf (int[] array, double target) { + int start = 0; + int end = array.length - 1; + while (start <= end) { + int mid = (start+end)/2; + if (target < array[mid]) { + end = mid - 1; + } else if (target > array[mid]){ + start = mid+1; + } else { //相等 + return mid; + } + } + return -1; +} +``` + +如果我们想在找不到时,返回当前数应该插入的位置index,那么应该这样 + +```java +public int findByHalf (int[] array, double target) { + int start = 0; + int end = array.length - 1; + while (start <= end) { + int mid = (start+end)/2; + if (target < array[mid]) { + end = mid - 1; + } else if (target > array[mid]){ + start = mid+1; + } + } + System.out.println("start"+start); + return start; + } +``` + +所以此题可以计算K+0.5应该插入的位置减去 K-0.5应该插入的位置,就得到K的个数了 +```java +public int GetNumberOfK(int [] array , int k) { + if (array==null||array.length== 0) { + return 0; + } + return findByHalf(array, k+0.5) - findByHalf(array, k-0.5) ; + } + public int findByHalf (int[] array, double target) { + int start = 0; + int end = array.length - 1; + while (start <= end) { + int mid = (start+end)/2; + if (target < array[mid]) { + end = mid - 1; + } else if (target > array[mid]){ + start = mid+1; + } + + } + return start; + } +``` + +## 题037 二叉树的深度 + +输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 + +递归来遍历就好了 + +``` + public int TreeDepth(TreeNode root) { + if (root==null) { + return 0; + } + int left = TreeDepth(root.left); + int right = TreeDepth(root.right); + return left > right ? left+1 : right+1; + } +``` + +## 题038 判断是否是平衡二叉树 + +平衡二叉树的特点就是任意节点的左右子树高度差的绝对值都小于等于1, + +也可以先根据上面计算二叉树深度的算法先计算左右高度差,然后再去减,判断当前节点是否满足平衡二叉树的要求,然后再去对左子节点,和右子节点做同样的操作,但是这样的问题在于会对节点多次重复遍历。如果是把顺序调换一下,先去分别计算左右子节点的最大高度,过程中,发现不符合平衡二叉树的要求时,直接返回-1,这样就直接结束了,否则返回最大高度。 + +```java +public boolean IsBalanced_Solution(TreeNode root) { + int depth = IsBalanced_Solution_Depth(root); + + return depth == -1 ? false : true;//返回-1代表有节点不满足平衡二叉树的要求 +} +public int IsBalanced_Solution_Depth(TreeNode root) { + if (root == null) { + return 0; + } + int left = IsBalanced_Solution_Depth(root.left); + int right = IsBalanced_Solution_Depth(root.right); + if (left!=-1 && right!= -1) { + int temp = left-right; + if (temp<=1 && temp>=-1) { + return left > right ? left + 1 : right + 1; + } + } + return -1;//返回-1代表有节点不满足平衡二叉树的要求 +} +``` + +## 题039 数组中只出现一次的数 + +一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 + + +数组中有两个数字只出现了一次,其他数字都出现了两次, + +因为A异或A的结果是0,所以对数组遍历异或后的结果result,出现两次的数字异或结果为0, 所以result其实是两个只出现一次的数字B和C的异或结果,并且因为B,C不相等,所以result肯定也不等于0,result肯定有一位是1,在这一位上,肯定B,C中一个为1,一个为0,所以可以根据这一位将数组分成两个子数组,这样每个子数组只会包含一些出现过两次的数字和B,C中的一个,所以对两个子数组异或只会的结果就可以得到B和C。 + +```java +public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { + int result = 0; + for (int i = 0; i < array.length; i++) { + result = result ^ array[i]; + } + //发现result第一个为1的位数 + int bit = 1; + int tempResult = result & bit; + while (tempResult==0) { + bit = bit*2; + tempResult = result & bit; + } + int number1 = 0; + int number2 = 0; + for (int i = 0; i < array.length; i++) { + int temp = array[i]&bit; + if (temp == 0) { + number1 = number1 ^ array[i]; + } else { + number2 = number2 ^ array[i]; + } + } + num1[0] = number1; + num2[0] = number2; +} +``` + +## 题040 和为S的连续正数序列 + +输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序 + +就是使用滑动窗口来实现,当两个下标相差1时,计算的和还比sum大,这个时候会进行low++,会使得low==high,跳出循环,例如sum是100,那么在low=high=51时跳出循环 + +```java +public ArrayList> FindContinuousSequence(int sum) { + ArrayList arrayList = new ArrayList>(); + int low=1,high=2; + while (low(); + for (int k =low;k<=high;k++) { + tempArrayList.add(k); + } + arrayList.add(tempArrayList); + low++; + } else if (currentSum FindNumbersWithSum(int [] array, int sum) { + ArrayList arrayList = new ArrayList(); + int low = 0; + int high = array.length-1; + + while (low=end) { + return str; + } + StringBuffer stringBuffer = new StringBuffer(str); + for (int i = start,j = end; i < j; i++,j--) { + char temp = stringBuffer.charAt(i); + stringBuffer.setCharAt(i, stringBuffer.charAt(j)); + stringBuffer.setCharAt(j, temp); + } + return stringBuffer.toString(); +} +``` + +## 题043 翻转单词的序列 + + +牛客最近来了一个新员工 Fish ,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么? + + +就是字符串整个都翻转过来,并且每个单词也要翻转过来 + +"student. a am I"字符串,处理后的结果需要是I am a student.,所以第一遍遍历对整个字符串进行翻转,第二遍,对每个单词进行翻转。 + +```java + public String ReverseSentence(String str) { + StringBuffer stringBuffer = new StringBuffer(str); + //先将字符串整体翻转 + for (int i = 0,j=stringBuffer.length()-1; i < j; i++,j--) { + char temp = stringBuffer.charAt(i); + stringBuffer.setCharAt(i, stringBuffer.charAt(j)); + stringBuffer.setCharAt(j, temp); + } + //对每个单词进行翻转 + int start=0,end=0; + int i = 0; + while (i=1 && numbers[i]!=0&&numbers[i-1]!=0) { + if (numbers[i] == numbers[i-1]) { + return false; + } + needKing += numbers[i] - numbers[i-1]-1; + } + } + if (needKing>count) { + return false; + } + return true; +} + +//快排写法 +public static void qsort(int[] array, int start ,int end) { + if (start>=end) {return;} + int threhold = array[start];//阀值 + int i = start; + int j = end; + while (i < j) { + while (array[j] >= threhold && i < j) j--;//从右端找的应该需要写在前面 + while (array[i] <= threhold && i < j) i++; + int temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + //将现在最中间的数与最左端的数(也就是阀值)交换 + array[start] = array[i]; + array[i] = threhold; + qsort(array, start, i - 1); + qsort(array, i + 1, end); + +} +``` + +## 题045 圆圈中最后剩下的数字 + + +每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF 作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数….这样下去….直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从 0 到 n-1 ) + + +就是约瑟夫环问题 + +假设一开始有n个数,每个人的编号为0到n-1,让数到m的人出局,假设出局的人的编号为k,那么k=(m-1)%n; +在n个人中间报数时,每个人的编号是 +0 1 2 ... k k+1 k+2 ... n-1 +当k出局以后,在n-1个人中报数时,每个人的编号是重新从k+1开始计数,原来的编号就映射为下面这样了(原来k+1变成了0,原来的n-1变成了n-1-(k+1)) +n-k-1 ... 0 1 ... n-1 -(k+1) +所以假设从n-1报数时的编号到n个人报数时的编号存在一个映射关系 +假设f(n)代表n个人报数编号 +f(n) = (f(n-1)+k+1)%n = (f(n-1)+ (m-1)%n + 1)%n = +(f(n-1) +m)%n + +n为1时,只有一个数,也就是最后剩下的数,所以f(1)为0 + +n>1时,f(n)=(f(n-1)+m)%n + +所以就是按照这个公式从2计算到n,得到f(n) + +```java +public int LastRemaining_Solution(int n, int m) { + if (n<1|| m<1) { + return -1; + } + int last = 0; + for (int i = 2; i<=n; i++) { + last = (last+m)%i; + } + return last; +} +``` + +## 题046 求1+2+…+n + +求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。 + +解题思路: + +因为不能使用for循环,肯定是需要使用递归来进行累加,但是由于不能使用if和三元判断语句,所以结束递归不是特别方便,所以这里是利用了&&语句,当n>0时,后面的语句才会执行,同时利用了赋值语句的值可以用于判断的特性,sum+=Sum_Solution(n-1))>0 + +``` +public int Sum_Solution(int n) { + int sum = n; + boolean ans = (n>0)&&((sum+=Sum_Solution(n-1))>0); + return sum; + } +``` + +## 题047 不用加减乘除做加法 + +写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。 + +就是A^B,A异或B的结果其实就是A,B二进制位中不相同的结果,也就是A+B,但是不进位的相加结果,A+B进位的那一部分结果等于(A&B)<<1,所以在循环中一直用A^B+A&B,一直到A&B等于0。 + + + +A+B = A^B + A&B<<1 + +如果A&B为0的话也就不需要进行进位了,此时就等于A+B = A^B,否则就继续递归调用方法,对A^B + A&B<<1 进行相加 + +递归写法 + +```java +public int Add(int num1,int num2) { + if(num2 == 0) { + return num1; + } + return Add(num1^num2, (num1&num2)<<1); +} +``` + +for循环写法 + +```java +public int Add(int num1,int num2) { + while (num2!=0) { + int temp = num1 ^ num2; + int temp2 = (num1 & num2) << 1; + num1 = temp; + num2 = temp2; + } + return num1; +} +``` + +## 题048 将字符串转换为整数 + +就是遍历,累乘,需要注意的是, + +1.String的substring(index1,index2)方法是一个这样的区间[index1,index2),也就是包含左边界,不包含右边界。所以在如果需要取某个字符串除去第一个字符以外的部分,应该是str.substring(1,str.length) + +2.int和Integer的取值范围是 + +-2147483648 至 2147483647 + +-2的31次方 2的31次方 -1 + +```java +public static int StrToInt(String str) { + if (str==null || str.length()==0) { + return 0; + } + int isNegative = 0; + if (str.charAt(0) == '+') { + str = str.substring(1,str.length()); + } else if (str.charAt(0) == '-') { + str = str.substring(1,str.length()); + isNegative = 1; + } + long sum =0; + for (int i = 0; i '9') {//字符不合法 + return 0; + } else { + sum = sum * 10 + c - '0'; + } + } + if (isNegative ==1) { + sum = 0-sum; + } + if (sum>Integer.MAX_VALUE || sum =0; i--) { + result2 = result2 * A[i+1]; + d[i] = result2; + } + for (int i = 0; i < b.length; i++) { + b[i] = c[i]*d[i]; + } + return b; +} +``` + +## 题053 字符流中第一个不重复的字符 + +就是使用一个数组来记录字符出现的次数。 + +```java + StringBuffer str = new StringBuffer(); + int[] table = new int[256];//记录出现次数,0代表0次,1代表1次,2代表2次及2次以上 + public void Insert(char ch) + { + if (table[ch] < 2) {//出现次数为0次和1次时才累加。 + table[ch]++; + } + str.append(ch); + } + +public char FirstAppearingOnce() +{ + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (table[c] == 1) { + return c; + } + } + return '#'; +} +``` + +## 题054 链表中环的入口节点 + +给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。 + + +一种方法是遍历整个链表,将每个节点添加到HashSet中,判断是否在HashSet中出现过,第一个重复的节点就是环的入口节点。 + +另一种解决方法是,假设存在环,环的长度为x,第一个指针先走x步,然后第二个指针从链表头结点出发,两个指针一起走,当第二个指针刚好走到环入口时,第一个指针正好在环中走了一圈,也在环的入口,此时的节点就是环的的入口节点, + +怎么得到环的长度呢,就是一个指针每次走2步,一个指针每次走一步,他们相遇时的节点肯定就是在环中的某个节点,然后这个节点在环中遍历一圈,回到原点,就可以得到环的长度count。 + +两个指针从头出发,第一个指针先走count步,然后两个指针每次都只走一步,相遇的地方就是环的入口。 + +```java +public ListNode EntryNodeOfLoop(ListNode pHead) +{ + if (pHead == null || pHead.next==null) { + return null; + } + //计算环的长度 + ListNode slowNode = pHead.next; + ListNode quickNode = slowNode.next; + ListNode nodeInLoop = null;//获取环上的某个节点 + while (quickNode!=null && slowNode!= null) { + if (quickNode == slowNode) { + nodeInLoop = quickNode; + break; + } + slowNode = slowNode.next; + quickNode = quickNode.next; + if (quickNode!= null) { + quickNode=quickNode.next; + } + } + if (nodeInLoop == null) {//说明没有环 + return null; + } + //根据环上的某个节点来计算环的长度count + ListNode tempNode = nodeInLoop; + int count = 1;//将当前计算计算在内 + while (tempNode.next!=nodeInLoop) { + tempNode = tempNode.next; + count++; + } + //从链表头结点出发,第一个指针先走count步,然后两个指针每次只走一步,相遇的地方就是环的入口, + // 然后第一个指针和第二个指针一起走,当第二个指针刚好走了x步到环入口时, + // 第一个指针正好走了x+count步,在环中走了一圈,也在环的入口, + quickNode = pHead; + for (int i = 0; i < count; i++) { + quickNode = quickNode.next; + } + + slowNode = pHead; + while (quickNode!=slowNode) { + quickNode = quickNode.next; + slowNode=slowNode.next; + } + return slowNode; +} +``` + +使用hashSet的解法 + +```java +public ListNode EntryNodeOfLoop1(ListNode pHead) +{ + if (pHead==null) { + return null; + } + HashSet set = new HashSet(); + ListNode node = pHead; + while (node !=null) { + if (set.contains(node)) { + return node; + } else { + set.add(node); + } + node= node.next; + } + return null; +} +``` + +## 题055 删除链表中重复的节点 + +在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5 + +解法: + +就先创建一个我们自己的节点ourHead, + +ourHead.next= head, + +pre = ourHead + +currentNode = pre.next + +然后currentNode开始向后遍历,每次拿当前节点与后一个节点值比较 + +相等,那么就遍历找到一个不相等的点,然后将pre节点指向这个不相等的节点,currentNode = pre.next + +不相等,那么就直接让pre和currentNode向后移动一步。 + +```java +public ListNode deleteDuplication(ListNode pHead) +{ + if (pHead == null || pHead.next == null) { + return pHead; + } + + ListNode ourHead = new ListNode(0); + ourHead.next = pHead; + ListNode preNode = ourHead; + ListNode currentNode = ourHead.next; + + while (currentNode!=null) {//往后遍历 + if (currentNode.next!=null && currentNode.val == currentNode.next.val) {//如果当前节点与下一个节点相等,就找到一个与当前节点不相等的节点,然后把中间多出来的这些相等的节点都删除掉 + ListNode tempNode = currentNode.next; + //找到第一个不相等的节点 + while (tempNode!=null) { + if(tempNode.val == currentNode.val) { tempNode = tempNode.next; } + else { break; } + } + preNode.next = tempNode; + currentNode = preNode.next; + } else {//如果当前节点与下一个节点相等,就跳过,遍历下一个节点 + preNode = preNode.next; + currentNode = currentNode.next; + } + } + return ourHead.next; +} +``` + +## 题056 二叉树的下一个节点 + +给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。 + + + +主要就是分情况讨论 + +中序遍历就是左子树,根节点,右子树 + +所以当前节点的中序遍历中的下一个节点是 + +1.当前节点有右子树 + +​ 右子树有左节点,一直向下遍历,找到最左的叶子节点。 + +​ 右子树没有左节点,就是右子树节点。 + +2.当前节点没有右子树 + +​ 没有父节点,那么没有下一个节点。 + + 这个节点有父节点 + +​ 这个节点父节点是属于左边分支的,直接返回父节点。 + +​ 这个节点父节点是属于右边分支的,一直向上遍历,直到找到一个父节点,他是祖先节点是左节点的,找到就返回祖先节点,找不到就返回空。 + +```java +public TreeLinkNode GetNext(TreeLinkNode pNode) +{ + //这个节点有右子树 + if (pNode.right != null) { + TreeLinkNode right = pNode.right; + if (right.left==null) {//右子树没有左节点 + return right; + } else { + TreeLinkNode leftNode = right.left; + while (leftNode.left!= null) {//右子树有左节点 + leftNode = leftNode.left; + } + return leftNode; + } + } else {//这个节点没有右子树,那么就去找父节点 + TreeLinkNode father = pNode.next; + if (father == null) {//父节点为空 + return null; + } else if(father.left == pNode) {//父节点不为空,该节点为父节点的左子树 + return father; + } else { + while (father.next!=null) { + TreeLinkNode grandFather = father.next; + if (grandFather.left == father) { + return grandFather; + } else { + father = grandFather; + continue; + } + } + } + } + return null; +} +``` + +## 题057 对称的二叉树 + +请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。 + + + +前序遍历是根节点,左子树,右子树 + +假设有另外一种遍历是根节点,右子树,左子树,如果二叉树是对称,那么这两种遍历的结果是一样的,所以使用递归来进行两种遍历,然后在过程中判断两种遍历结果是否一样。 + +```java +boolean isSymmetrical(TreeNode pRoot) +{ + return isSymmetrical(pRoot,pRoot); +} + +boolean isSymmetrical(TreeNode leftRoot,TreeNode rightRoot) +{ + + if (leftRoot == null || rightRoot == null) { + return (leftRoot == null) & (rightRoot == null); + } + if (leftRoot.val!=rightRoot.val) { + return false; + } else { + return isSymmetrical(leftRoot.left, rightRoot.right) + & isSymmetrical(leftRoot.right, rightRoot.left); + } +} +``` + +## 题058 按之字形顺序打印二叉树 + +就是使用两个栈,stack1存放奇数层的节点,stack2存放偶数层的节点,一开始将根节点加入奇数层的栈,开始遍历, + +当前处于奇数层时,每次对stack1出栈,将出栈的节点的值打印,然后依次将节点的左子节点,右子节点加入到stack2,一直到stack1的全部元素出栈。 + +当前出于偶数层时,每次对stack2出栈,将出栈的节点的值打印,然后依次将节点的右子节点,左子节点加入大屏stack2,一直到stack2的全部元素出栈。 + +```java +public ArrayList> Print(TreeNode pRoot) { + ArrayList> arrayLists = new ArrayList>(); + if (pRoot==null) return arrayLists; + Stack stack1 = new Stack();//存放奇数层的栈 + Stack stack2 = new Stack();//存放偶数层的栈 + int flag = 0;//代表当前遍历的是奇数层还是偶数层。区别在于添加子节点的顺序。 + stack1.add(pRoot); + while ((flag == 0 && stack1.size()>0) || (flag == 1 && stack2.size()>0)) { + if (flag==0) {//代表是偶数层 + ArrayList array = new ArrayList(); + while (stack1.size()>0) { + TreeNode node = stack1.pop(); + array.add(node.val); + if (node.left!=null) stack2.push(node.left); + if (node.right!=null) stack2.push(node.right); + } + arrayLists.add(array); + flag = 1; + } else { + ArrayList array = new ArrayList(); + while (stack2.size()>0) { + TreeNode node = stack2.pop(); + array.add(node.val); + if (node.right!=null) stack1.push(node.right); + if (node.left!=null) stack1.push(node.left); + } + arrayLists.add(array); + flag = 0; + } + } + return arrayLists; +} +``` +这是另外一种写法。 +```java +public ArrayList> Print(TreeNode pRoot) { + ArrayList> list = new ArrayList>(); + Stack otherStack = new Stack(); + Stack currentStack = new Stack(); + if(pRoot == null) { + return list; + } + currentStack.push(pRoot); + int addChildFromRightFlag = 0;// + while(currentStack.size()>0 || otherStack.size()>0) { + ArrayList array = new ArrayList(); + while(currentStack.size()>0) { + + TreeNode node = currentStack.pop(); + array.add(node.val); + if(addChildFromRightFlag == 0) {//根据层数的不同,决定从右边还是左边添加节点。 + if(node.left!=null) { + otherStack.add(node.left); + } + if(node.right!=null) { + otherStack.add(node.right); + } + } else { + if(node.right!=null) { + otherStack.add(node.right); + } + if(node.left!=null) { + otherStack.add(node.left); + } + } + } + list.add(array); + addChildFromRightFlag = addChildFromRightFlag == 0 ? 1:0; + currentStack = otherStack; + otherStack = new Stack(); + } + return list; + } +``` + + + +## 题059 把二叉树打印成多行 + +宽度优先遍历的话,就是使用一个队列来实现,这里需要每一层的节点在一行打印,其实就是宽度优先遍历时需要区分每一层。 + +使用两个队列的解法 + +可以使用两个队列,队列queue1存放奇数层节点,队列queue2存放偶数层节点,一开始将根节点加到队列queue1,然后对queue1所有元素按顺序出列,每次将元素的左右子节点添加到偶数队列queue2中,知道queue1队列元素全部出列,然后对queue2队列重复queue1的操作,直到queue1,queue2的元素都为空。 + +使用一个队列的解法 + +就是使用一个队列queue,一开始将根节点加入queue,并且加入一个null元素到队列中作为标志元素,用来分割每一层,标志这一层的节点都在标志元素的前面。然后对queue中元素出列,每个进行打印,直到出列的元素是null,表示这一层已经结束了,如果queue中还有元素,那么在后面加入null标志元素分割,并且进行换行,打印下一行,如果queue中没有元素就结束循环 + +```java +ArrayList> Print(TreeNode pRoot) { + ArrayList> arrayLists= new ArrayList>(); + if (pRoot == null) return arrayLists; + ArrayList queue = new ArrayList<>(); + queue.add(pRoot); + queue.add(null);//每一层结束时,添加标志节点 + ArrayList tempArrayList = new ArrayList<>(); + while (queue.size()>0) { + TreeNode treeNode = queue.remove(0); + if (treeNode==null) {//null是标志节点,说明这一层已经打印结束了 + arrayLists.add(tempArrayList); + tempArrayList = new ArrayList<>(); + if (queue.size() == 0) {break;}//如果队列里没有元素了,就结束循环 + else { queue.add(null); }//如果队列里还有元素就继续添加标志节点 + } else { + tempArrayList.add(treeNode.val); + if (treeNode.left!=null) queue.add(treeNode.left); + if (treeNode.right!=null) queue.add(treeNode.right); + } + } + return arrayLists; +} +``` + +递归的解法 + +就是递归遍历每一个节点,遍历时传入深度depth,将节点加入到ArrayList中特定深度对应的数组中去。 + +这种方法也可以用来进行二叉树先序遍历,遍历完之后将嵌套数组拆分成单层的数组。 + +```java +ArrayList> Print2(TreeNode pRoot) { + ArrayList> arrayLists = new ArrayList>(); + if (pRoot==null)return arrayLists; + find(pRoot,1,arrayLists); + return arrayLists; +} + +void find(TreeNode pRoot, int depth, ArrayList> arrayLists) { + if (arrayLists.size()< depth) {//还没有存放这一层节点的数组 + arrayLists.add(new ArrayList()); + } + ArrayList tempArrayList = arrayLists.get(depth-1); + tempArrayList.add(pRoot.val); + if (pRoot.left!=null) find(pRoot.left,depth+1,arrayLists); + if (pRoot.right!=null) find(pRoot.right,depth+1,arrayLists); +} +``` + +## 题060序列化二叉树 + +请实现两个函数,分别用来序列化和反序列化二叉树 + + + +序列化的过程就是二叉树宽度遍历的过程,null元素也会加入到序列化的字符串中去 + +反序列化就是调用String.split()方法 + +1.将字符串先转换为String[]数组的过程, + +2.然后也是去数组取出第一个字符串firstStr,创建一个根节点treeNode,将firstStr转换为Integer类型,赋值给treeNode,然后将treeNode添加到一个队列中去。 + +3.每次从队列取出一个元素node,依次从String[]数组中取两个值,转换为Intger类型,作为node的左,右子节点,如果左,右子节点的值不为null,那么就将左右子节点添加到队列中去。 + +4.重复步骤3,直到队列元素个数为空。 + +```java +String Serialize(TreeNode root) { + StringBuffer stringBuffer = new StringBuffer(); + if (root == null) {return stringBuffer.toString();} + ArrayList queue = new ArrayList(); + queue.add(root); + while (queue.size()>0) { + TreeNode node = queue.remove(0); + if (node == null) { + stringBuffer.append("#!"); + } else { + stringBuffer.append(node.val+"!"); + queue.add(node.left); + queue.add(node.right); + } + } + return stringBuffer.toString(); +} + +TreeNode Deserialize(String str) { + if (str == null || str.length() == 0) {return null;} + String[] array = str.split("!"); + + Integer rootValue = convert(array[0]); + if (rootValue == null) {return null;} + + TreeNode rootNode = new TreeNode(rootValue); + ArrayList queue = new ArrayList(); + queue.add(rootNode); + int currentIndex = 1; + while (queue.size()>0 && currentIndex左子树所有节点,根节点<右子树所有节点 + +由于前序遍历是先左子树,根节点,右子树的顺序,所以前序遍历的结果,就是二叉搜索树中元素按递增顺序排列的结果,所以按照前序遍历到第K个元素就是第K小的节点。 + +```java +Integer index = 0; +TreeNode kNode = null; + +TreeNode KthNode(TreeNode pRoot, int k) +{ + find(pRoot,k); + return kNode; +} + +void find(TreeNode node, Integer k) { + if (node == null) {return;} + find(node.left, k); + index++; + if (index == k) { + kNode = node; + } else { + find(node.right, k); + } +} +``` + +## 题062 数据流的中位数 + +如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用 `Insert()` 方法读取数据流,使用 `GetMedian()` 方法获取当前读取数据的中位数。 + + + +可以使用插入排序来实现,就是每次插入元素,将元素与排好序的元素逐一比较,插入已排序后的数组,返回中位数时直接根据下标来获取 + + + +```java + ArrayList arrayList = new ArrayList(); + + public void Insert(Integer num) { + if (arrayList.size()==0) { + arrayList.add(num); + } else { + for (int i = arrayList.size()-1; i >=0 ; i--) { + if (arrayList.get(i)=num && i==0) {//插入的元素比数组中所有元素都小的情况 + arrayList.add(0,num); + } + } + } + } + + public Double GetMedian() { + if (arrayList.size()%2==1) { + return arrayList.get(arrayList.size()/2).doubleValue() ; + } else { + return (arrayList.get(arrayList.size()/2-1).doubleValue() + arrayList.get(arrayList.size()/2).doubleValue())/2; + } + } + +``` + +## 题063 滑动窗口的最大值 + +给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组`{2,3,4,2,6,2,5,1}`及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为`{4,4,6,6,6,5}`; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: `{[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}`。 + +解法一就是维护一个排序好的数组,数组就是当前滑动窗口排序好的结果,每次滑动时将值插入排序好的队列,并且将过期的值删除, + +```java +public ArrayList maxInWindows1(int[] num, int size) { + ArrayList arrayList = new ArrayList(); + ArrayList sortList = new ArrayList(size); + + if (num == null || num.length == 0 || size <= 0 || size > num.length) { + return arrayList; + } + int i = 0; + for (; i < size; i++) {//初始化一开始的滑动窗口的值 + sortList = insertValueIntoSorted(sortList, num[i]); + } + arrayList.add(sortList.get(size - 1));//添加第一个滑动窗口的最大值 + for (; i < num.length; i++) { + sortList = removeValueIntoSorted(sortList, num[i - size]);//将离开滑动窗口的值从排序数组移除 + sortList = insertValueIntoSorted(sortList, num[i]); + arrayList.add(sortList.get(size - 1)); + } + return arrayList; +} + +ArrayList insertValueIntoSorted(ArrayList sortList, int currentValue) { + if (sortList.size() == 0) { + sortList.add(currentValue); + } else { + for (int j = sortList.size() - 1; j >= 0; j--) { + if (currentValue > sortList.get(j)) { + sortList.add(j + 1, currentValue); + break; + } else if (j == 0) { + sortList.add(0, currentValue); + } + } + } + return sortList; +} + +ArrayList removeValueIntoSorted(ArrayList sortList, int currentValue) { + int index = 0; + for (int i = 0; i < sortList.size(); i++) { + if (sortList.get(i) == currentValue) { + index = i; + break; + } + } + sortList.remove(index); + return sortList; +} +``` + +解法二使用双端队列来实现,队列是按大小顺序排列的,头结点的值最大 +每次往队列中添加元素A时,先将比当前元素A小的所有元素都丢掉, +因为这些元素的index比A都小,而且值也比A小,不可能再成为最大值了, +然后判断队列头结点的最大值是否过期,过期的话也删除 + +```java + +public ArrayList maxInWindows(int[] num, int size) { + ArrayList arrayList = new ArrayList(); + if (num == null || num.length == 0 || size <= 0 || size > num.length) { + return arrayList; + } + LinkedList maxQueue = new LinkedList<>();//存储最大值的下标 + for (int i = 0; i < num.length; i++) { + int begin = i - size + 1;//滑动窗口最左边的下标 + + if (maxQueue.size() == 0) { + maxQueue.add(i); + } + if (maxQueue.peekFirst() < begin) {//说明已经过期 + maxQueue.pollFirst();//将它移除队列 + } + while (maxQueue.size() > 0 && num[maxQueue.peekLast()] <= num[i]) {//队列末尾的值都小于当前值,将他们都出列 + maxQueue.pollLast();//出列 + } + maxQueue.add(i); + if (begin >= 0) {//滑动接口全部处于在数组中时才能添加值 + arrayList.add(num[maxQueue.peekFirst()]); + } + } + return arrayList; +} +``` + +## 题064 矩阵中的路径 + +请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 `a b c e s f c s a d e e` 这样的 `3 X 4` 矩阵中包含一条字符串`"bcced"`的路径,但是矩阵中不包含`"abcb"`路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。 + + + +判断一个字符串在字符矩阵中是否一条符号条件的路径 + +就是递归去判断就行了。 + +```java +public boolean hasPath(char[] matrix, int rows, int cols, char[] str) +{ + boolean[] flag = new boolean[matrix.length]; + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (matrix[i*cols + j] == str[0]) { + if (judge(matrix,rows,cols,i, j, str, 0, flag)) { + return true; + } + } + } + } + return false; +} + +//flag 标识这个节点是否已经在路径中了 + + +boolean judge(char[] matrix, int rows, int cols, int i, int j, char[] str, int charIndex,boolean[] flag) { + + + int index = i*cols + j; + if (i<0 || j<0 || i>=rows || j>=cols || matrix[index] != str[charIndex] || flag[index] == true) { + return false; + } + if (charIndex==str.length-1) { return true;} + //用来记录这个位置是否在路径中 + flag[index]=true; + if (judge(matrix,rows,cols,i+1, j, str, charIndex+1, flag) + ||judge(matrix,rows,cols,i-1, j, str, charIndex+1, flag) + ||judge(matrix,rows,cols,i, j+1, str, charIndex+1, flag) + ||judge(matrix,rows,cols,i, j-1, str, charIndex+1, flag)) { + return true; + } + + flag[index]=false; + return false; +} +``` + +## 题065机器人的运动范围 + +地上有一个m行和n列的方格。一个机器人从坐标 `0,0` 的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为 `18` 时,机器人能够进入方格`(35,37)`,因为`3+5+3+7 = 18`。但是,它不能进入方格`(35,38)`,因为`3+5+3+8 = 19`。请问该机器人能够达到多少个格子? + + + +地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 + +(就是行坐标的每位上的数字之和+坐列标的每位上的数字之和<=k) + + 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子? + +就是递归去求解,判断每个节点的上,下,左,右节点是否满足需求。 + +```java +public int movingCount(int threshold, int rows, int cols) +{ + if (rows<0||cols<0||threshold<0) {return 0;} + boolean[][] flagArray = new boolean[rows][cols]; + int count = findCount(rows,cols,0,0,flagArray,threshold); + return count; +} + +int findCount(int rows, int cols,int i,int j, boolean[][]flagArray,int threshold) { + if (i<0||i>=rows||j<0||j>=cols||flagArray[i][j]==true|| judge(i)+judge(j)>threshold) { + return 0; + } + flagArray[i][j]=true; + return 1+findCount(rows,cols,i-1,j,flagArray,threshold) + +findCount(rows,cols,i+1,j,flagArray,threshold) + +findCount(rows,cols,i,j-1,flagArray,threshold) + +findCount(rows,cols,i,j+1,flagArray,threshold); +} + +int judge(int i) { + int sum = 0; + while (i>0) { + sum += i%10; + i = i/10; + } + return sum; +} +``` + +## 题066剪绳子 + +给定一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为`k[0],k[1],…,k[m]`。请问`k[0]* k[1] * … *k[m]`可能的最大乘积是多少? + + + +就是把一个整数n,拆成m个整数,m个整数的乘积最大,根据动态规划的算法就是 + +f(1)=0; + +f(2)=1; + +f(3)=2; + +f(4)= 4; + +对于f(n) = max f(i)*f(n-i) 对i从0到n-1进行遍历,计算出最大值就是f(n),这样子就可以得到f(n)的值,时间复杂度是n^2,空间复杂度是n。 + + + +贪心算法就是根据公式计算当n>=5时,3(n-3)>=2(n-2),并且3(n-3)>=4(n-4),所以绳子就尽可能得去剪出3来,如果最后剪掉3,剩下1,就把最后的4按照,2和2来剪,这样会好一些。 + +```java +public int cutRope(int target) { + if (target <= 1) { + return 0; + } else if (target==2) { + return 1; + } else if (target == 3) { + return 2; + } + int result = 1; + int times = target/3; + int number = target%3; + if (number==1) {//就是余数为1时,给它一个3,凑成一个4,改成2*2乘积更大 + times=times-1; + result = result * 4; + for (int i = 0; i < times; i++) { + result = result * 3; + } + } else if (number == 2) { + result = result * 2; + for (int i = 0; i < times; i++) { + result = result * 3; + } + } + return result; +} +``` + +# 基础篇 + +## 二分查找 + +普通的二分查找 + +```java +public int findByHalf(int[] array, int target) { + if (array==null||array.length==0) { + return -1; + } + int left = 0; + int right = array.length-1; + while (left<=right) {//需要注意是小于等于right,否则会是[left,right),将最后一个元素排除比较 + int mid = left + (right - left)/2; + if (array[mid] == target){ + return mid; + } else if (array[mid]= array.length || array[left] != target){ return -1;} + return array[left]==target ? left : -1; + } + // 检查出界情况,因为循环结束的条件是left=right+1,如果target比数组所有元素都大会导致越界 + //如果有左边界就返回左边界,没有时,此时的left应该是最接近左边界,并且大于左边界的数的位置 +``` + diff --git a/docs/Coding_Array.md b/docs/Coding_Array.md new file mode 100644 index 0000000..940ecc3 --- /dev/null +++ b/docs/Coding_Array.md @@ -0,0 +1,1236 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +## 数组专题 + +### 剑指Offer部分 + +#### [题003 二维数组中的查找](#题003) + +#### [题008旋转数组](#题008) + +#### [题012调整数组顺序使奇数排在前面](#题012) + +#### [题018顺时针打印矩形](#题018) + +#### [题027数组中出现的次数超过一半的数字](#题027) + +#### [题028最小的k个数](#题028) + +#### [题029连续子数组的最大和](#题029) + +#### [题030从1到n中出现的整数中1出现的次数](#题030) + +#### [题031把数组排成最小的数](#题031) + +#### [题032返回第N个丑数](#题032) + +#### [题034 数组中的逆序对](#题034) + +#### [题036 数字在排序数组中出现的次数](#题036) + +#### [题039 数组中只出现一次的数组](#题039) + +#### [题040 和为S的连续正数序列](#题040) + +#### [题041 和为S的两个数字](#题041) + +#### [题044 扑克牌顺子](#题044) + +#### [题049 数组中重复的数字](#题049) + +#### [题050 构建乘积数组](#题050) + +#### [题062 数据流的中位数](#题062) + +#### [题063 滑动窗口的最大值](#题063) + +#### [题064 矩阵中的路径](#题064) + +### 题003 二维数组中的查找 + +##### 题目内容: + +![image-20201202111714727](../static/image-20201202111714727.png) + +在一个二维[数组](https://cuijiahua.com/blog/tag/数组/)中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维[数组](https://cuijiahua.com/blog/tag/数组/)和一个整数,判断数组中是否含有该整数。 + +如果在一个二维数组中找到数字7,则返回true,如果没有找到,则返回false。 + +##### 思路 + +就是从右上角开始遍历,假设要查找的数为A,当前遍历的数为B,B的特点是B所在行里面最大的数,也是B所在列最小的数,如果遍历的数BA,那么B所在的列可以排除(比B都大)。 + +##### 代码: + +```java + static boolean find(int target, int [][] array) { + int rowLength = array.length;//总行数 + int colLength = array[0].length;//总列数 + + int currentRow = 0;//起始遍历位置是右上角,行号为0 + int currentCol = colLength - 1;//起始遍历位置是右上角,列号为最大值 + while (currentRow = 0) {//防止超出边界 + if (array[currentRow][currentCol] == target) { + return true; + } else if (array[currentRow][currentCol] > target) {//比要找的数大,那么排除更大的数,也就是排除这一列 + currentCol--; + } else {//比要找的数小,那么排除更小的数,也就是排除这一行 + currentRow++; + } + } + return false; +} +``` + +##### 总结 + +注意这个currentRow = 0判断条件,防止越界。 + +## 题008旋转数组 + +把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。 + +##### 解题思路: + +旋转数组其实就是一个递增的数组,整体移动了一下元素,类似3,4,5,1,2这种。要查找最小的元素,可以遍历一遍数组,复杂度为O(N),这样就太暴力了,因为这个旋转数组其实是有规律的,可以根据左边界,右边界,中间值来判断最小值的位置 + +* 左边界<=中间值 说明左边界到中间值这一段是递增的,也就是最小值不处于这一段。这样可以排除掉这一段,然后去另一段里面遍历查找。 + +* 中间值<=右边界 说明中间值到右边界这一段是递增的,也就是最小值不处于这一段。这样可以排除掉这一段,然后去另一段里面查找。 + + 一直排除到最后,右边界下标-左边界下标==1时,说明左边界是最大值,右边界是最小值,此时整个循环结束。 + +* 特殊情况 左边界== 中间值==右边界 说明无法判断最小值位于哪里,只能从左边界到右边界进行遍历然后获得最小值。 + +```java +int minNumberInRotateArray(int[] array) { + if (array[0]= array[end]为基础的,一旦右边界值>左边界值,说明现在的顺序就是完全递增的,那么就返回左边界。 + while (array[start] >= array[end]) { + System.out.println(start+"======"+mid+"====="+end); + if (end-start == 1) { + return array[end]; + } + mid = (end + start)/2; + if (array[start] == array[mid] && array[start] == array[end]) {//左边界,中间值,右边界相等 + int min = array[start]; + for (int i = start+1; i <=end ; i++) { + if (array[i]< min) { + min = array[i]; + } + } + return min; + } + if ( array[mid]>=array[start]){ + start = mid; + } else if(array[mid]<=array[end]) { + end = mid; + } + } + return array[mid]; + } +``` + +## 题012调整数组顺序使奇数排在前面 + +输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 + +##### 使用额外的内存空间的解法 + +如果可以使用额外的内存空间,可以对数组遍历两遍,一遍将奇数取出,存放在额外的数组中去,一遍把剩下的偶数存放到额外的数组中去。 + +##### 类似冒泡排序的解法 + +如果不能使用额外的内存空间,就是查找到第一个奇数,然后与前面的偶数元素整体往后挪一位,将该奇数设置到前面第一个偶数的位置,有点像是冒泡排序,复杂度其实是O(n^2)。(由于本题需要保证奇数与奇数的顺序,偶数与偶数的顺序,所以需要使用这种类似于冒泡排序的算法,如果不保证顺序,找到奇数后就于第一个偶数交换位置就行,复杂度是O(N)) + +冒泡排序是其实是交换,从头开始,依次判断两个相邻的元素,将更大的元素向右交换,遍历一次后可以将当前序列最大的元素交换到最后面去,下次遍历就不用管最后一个元素。 + +```java + public static void reOrderArray1(int [] array) { + if (array==null || array.length==0) { + return; + } + int saveIndex=0;//第一个偶数的位置,用于存后面找到的第一个奇数 + for (int i = 0; i < array.length; i++) { + if (array[i]%2==1) {//找到奇数 + int temp = array[i];//将奇数保存 + //将奇数前面的数都往后挪 + for (int j = i; j >saveIndex; j--) { + array[j] = array[j-1]; + } + array[saveIndex] = temp; + saveIndex++; + } + } + } +``` + +## 题018 顺时针打印矩形 + +输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: +1 2 3 4 +5 6 7 8 +9 10 11 12 +13 14 15 16 +则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10. + +就是将矩形外面一圈打印完,外面打印时可以分为四块,上面,右边,下面,左边。然后递归打印剩下的部分,当矩阵是正常情况下时,这么打印是没有问题,如果矩阵只有一个元素,只有一行,只有一列,这么打印就会有问题,所以我们将特殊情况单独列出来了。 + +```java + public ArrayList printMatrix2(int [][] matrix) { + ArrayList arrayList = new ArrayList(); + printMatrix2(arrayList,matrix,0,matrix.length-1,0,matrix[0].length-1); + return arrayList; + } + public ArrayList printMatrix2(ArrayList arrayList, int [][] matrix,int rowStart,int rowEnd,int colStart,int colEnd) { + if (rowStart>rowEnd||colStart>colEnd) { + return arrayList; + } + //我们采取的策略是按照左闭右开区间进行打印,也就是[rowStart,roWend),对于每一边打印时, + // 只包含左边界的值,不包含右边界的值 + // 比如二维矩阵为 + // 1 2 3 + // 4 5 6 + // 7 8 9 + // 打印顺序是先打印 + // 1,2 + //3,6 + //9,8 + //7,4 + //4 + //这种打印方法的缺点是只有一个元素,只有一行,只有一列时,打印就会有问题,所以我们将这些情况单独列出来。 + //特殊情况1 只有一个元素 + if (rowStart == rowEnd && colStart == colEnd) { + arrayList.add(matrix[rowStart][colEnd]); + return arrayList; + } + //特殊情况2 只有一列, + if (colStart == colEnd) { + for (int i = rowStart; i <=rowEnd ; i++) { + arrayList.add(matrix[i][colStart]); + } + return arrayList; + } + //特殊情况2 只有一行 + if (rowStart == rowEnd) { + for (int i = colStart; i <= colEnd; i++) { + arrayList.add(matrix[rowStart][i]); + } + return arrayList; + } + //打印上边 + for (int i = colStart;icolStart;i--) { + arrayList.add(matrix[rowEnd][i]); + } + //打印左边 + for (int i = rowEnd;i>rowStart;i--) { + arrayList.add(matrix[i][colStart]); + } + printMatrix2(arrayList,matrix,rowStart+1,rowEnd-1,colStart+1,colEnd-1); + return arrayList; + } +``` + +##### 解法二: + +```java +public ArrayList printMatrix(int [][] matrix) { + if(matrix==null) { + return null; + } + ArrayList arrayList = new ArrayList(); + return printMatrix(arrayList, matrix, 0, matrix.length-1,0,matrix[0].length -1); + } + + public ArrayList printMatrix(ArrayList arrayList, int [][] matrix,int rowStart, int rowEnd, int colStart, int colEnd) { + if (rowStart> rowEnd || colStart>colEnd) { + return arrayList; + } + for (int i = colStart; i <=colEnd;i++) { + arrayList.add(matrix[rowStart][i]); + } + for (int i = rowStart+1; i <=rowEnd-1;i++) { + arrayList.add(matrix[i][colEnd]); + } + for (int i = colEnd; i >=colStart&&rowEnd>rowStart;i--) {//要加rowEnd>rowStart判断,不然对于单行情况会重复打印 + arrayList.add(matrix[rowEnd][i]); + } + for (int i = rowEnd-1; i >=rowStart+1&& colStart < colEnd;i--) {//要加rowEnd>rowStart判断,不然对于单列情况会重复打印 + arrayList.add(matrix[i][colStart]); + } + printMatrix(arrayList, matrix,rowStart+1,rowEnd-1, colStart+1,colEnd-1); + return arrayList; + } +``` + +## 题027数组中出现的次数超过一半的数字 + +数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出 2 。如果不存在则输出 0 。 + +#### 解题思路: + +##### 只适用于本题的特殊解法 + +就是一个数组,假设包含一个超过次数一半的元素,那么去除掉两个不相等的元素后,剩下的数组中,这个元素还是会出现次数超过一半。 + +(原理就是每次排除两个不相等的元素,最后剩下的一个元素,或者两个元素一定是次数超过一半的这个数字。) + +```java +public int MoreThanHalfNum_Solution(int [] array) { + if (array==null||array.length==0) { + return 0; + } + if (array.length==1) {//只有一个元素,直接返回 + return array[0]; + } + int result = array[0]; + int times = 1; + for (int i = 1; i < array.length; i++) { + if (times == 0) { + times = 1; + result = array[i]; + } else if (array[i] == result) {//相同就是times+1 + times++; + } else {//不相同就将times-1,进行抵消 + times--; + } + } + //下面就是判断这个数字是否满足条件,因为也有可能存在1,1,2,2,3这种数组,最后导致result是3,但是3其实不是超过一半的元素,所以需要重新遍历判断。 + int statTimes = 0; + for (int i = 0; i < array.length; i++) { + if (array[i] == result) { + statTimes++; + } + } + if (statTimes>array.length/2) { + return result; + } + return 0; +} +``` + +##### 快排解法 + +另外一种思路就是假设该数组存在数量超过一半的元素A,并且数组排好序,那么元素A一定是数组的中位数,A的下标一定是n/2,所以此题可以转换为找出数组第n/2小的元素,找出元素A后,然后对数组进行遍历,判断A的出现次数是否超过了一半。但是这种解法的最坏时间复杂度是O(N^2); + +```java +//[1,2,3,2,4,2,5,2,3] + public int MoreThanHalfNum_Solution1(int[] array) { + if (array==null||array.length==0) { + return 0; + } + if (array.length==1) { + return array[0]; + } + int value = quickSort(array,0,array.length-1,array.length/2); + int times = 0; + for (int i = 0; i < array.length; i++) { + if (array[i] == value) { + times++; + } + if (times>array.length/2) { + return value; + } + } + return 0; + } + //寻找第K小的元素 + public int quickSort(int[] array,int start ,int end,int K) { + if (start>=end) { + return array[start]; + } + int base = array[start]; + int i = start; + int j = end; + while (ibase&&j>i) {j--;} + while (array[i]<=base&&iK) {//第K小的元素在左边 + return quickSort(array,start, i-1,K); + } else {//第K小的元素在右边 + return quickSort(array,i+1,end,K); + } + } +``` + +## 题028最小的k个数 + +输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。 + +##### 插入排序普通版 + +其实就是插入排序,只不过只是对k个数进行插入排序,复杂度O(N^2) + +```java +public ArrayList GetLeastNumbers_Solution(int [] input, int k) { + ArrayList arrayList = new ArrayList(); + if(input==null || input.length==0 ||input.length arrayList.get(arrayList.size()-1)) {//子数组个数达到了K,并且当前数比子数组最后一个数大 + continue; + } else if (input[i] < arrayList.get(arrayList.size()-1)) { + arrayList.remove(arrayList.size()-1); + arrayList.add(input[i]); + } + + //将最后一个元素移动合适的位置 + for (int j = arrayList.size()-1; j > 0 ; j--) { + if (arrayList.get(j) < arrayList.get(j-1)) { + int temp = arrayList.get(j); + arrayList.set(j, arrayList.get(j-1)); + arrayList.set(j-1, temp); + } + } + + } + return arrayList; + } +``` + +##### 解法二 插入排序优化版(根据二分查找插入的位置) + +时间复杂度N*log(N) + +```java +public ArrayList GetLeastNumbers_Solution1(int [] input, int k) { + ArrayList arrayList = new ArrayList<>(); + if (input==null||input.lengthk) { + arrayList.remove(arrayList.size()-1); + } + } + return arrayList; +} +//使用二分查找插入的位置 +void addNewElement(ArrayList arrayList, int target) { + if (target<=arrayList.get(0)) { + arrayList.add(0,target); + } else if (target>= arrayList.get(arrayList.size()-1)) { + arrayList.add(target); + } else {//使用二分查找,查找左边界,就是左边界右边的值就是>=target的 + int left = 0; + int right = arrayList.size(); + while (left target) { + //因为是寻找左边界,此时middle可能是需要的下标 + right = middle; + } + } + arrayList.add(left,target); + + } +} +``` + +##### 解法三 堆排 + +```java +public ArrayList GetLeastNumbers_Solution2(int [] input, int k) { + ArrayList arrayList = new ArrayList<>(); + if (input == null || input.length==0|| input.length=0 ; i--) { + adjustHeap(input,i,k); + } + for (int i = k; i < input.length; i++) { + if (input[i] < input[0]) { + swap(input,0,i); + adjustHeap(input,0,k); + } + } + for (int i = k-1; i >=0; i--) { + arrayList.add(input[i]); + } + return arrayList; +} +void adjustHeap(int[] input, int i ,int length) { + while (2*i+1 input[left] ? right:left; + maxIndex = input[i] > input[maxIndex] ? i : maxIndex; + if (maxIndex==i) { + break; + } else if (maxIndex == left) { + swap(input, i, left); + i = left; + } else if (maxIndex == right) { + swap(input, i, right); + i = right; + } + } else { + if (input[left] > input[i]) { + swap(input,i,left); + i = left; + } else { + break; + } + } + } +} +void swap(int[] input, int i, int j) { + int temp = input[i]; + input[i] = input[j]; + input[j] = temp; +} +``` + +##### 快排解法 + +平均时间复杂度是O(N),最坏的时间复杂度是O(N^2)。 + +最坏的时间复杂度出现的情况是当数组是以排好序的数组,每次取的基准元素都比数组中其他元素小,这样就每次分割就只能分割出1个元素,每次分割时间复杂度为O(N),需要分割K次,当K趋近与N时,复杂度就是O(N^2)。 + +```java +public ArrayList GetLeastNumbers_Solution3(int[] input, int k) { + ArrayList arrayList = new ArrayList(); + if (input==null||input.length=end) { + return; + } + int base = array[start]; + int i = start; + int j = end; + while (ibase&&j>i) {j--;} + while (array[i] <= base &&j>i) {i++;} + if (iK-1) { + quickSort(array,start,i-1,K); + } +} +``` + +## 题029连续子数组的最大和 + +例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和(子向量的长度至少是1) + +##### 解法一 + +```java + public int FindGreatestSumOfSubArray(int[] array) { + if(array==null || array.length==0) { + return 0; + } + int max=array[0];//最大的连续子数组和 + int currentSum = array[0];//前面的节点当前的累计值 + for (int i = 1; i < array.length; i++) { + if (currentSum<0) {//累计值<0,直接丢掉 + currentSum = array[i]; + } else {//累计值>0,不管现在的值是否为正数,都需要累加,不然就断了 + currentSum = currentSum + array[i]; + } + //超过最大值,保存到max + max = currentSum>max ? currentSum : max; + } + return max; + } +``` + +##### 动态规划的解法 + +使用动态规划的方法来进行思考 + +f(n) 有两种取值 + +* 当f(n-1)<=0时,取array[n] + +* 当f(n-1)>0时,取f(n-1)+array[n] + + 所以可以使用动态规划来解这道题。 + +## 题030从1到n中出现的整数中1出现的次数 + +求出1~13的整数中 1 出现的次数,并算出 100~1300 的整数中1出现的次数?为此他特别数了一下 1~13 中包含1的数字有 1、10、11、12、13 因此共出现 6 次,但是对于后面问题他就没辙了。ACMer 希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。 + +##### 解法一 + +就是对1到n进行遍历,对每个数统计该数1出现的次数,统计时用这个数x%10,判断个位数是否为1,然后用x=x/10的结果继续%10来进行判断个位数为1,一直到x=0,统计到x包含1的个数,这样的话,一共有N个数,每个数计算的时间复杂度log10 N,总时间复杂度是N*log10 (N)也就是Nlog(N) + +```java +public int NumberOf1Between1AndN_Solution2(int n) { + if (n<=0) {return 0;} + int count = 0; + for (int i = 1; i < n; i++) { + while (i>0) { + if (i%10==1) { + count++; + } + i=i/10; + } + } + return 0; +} +``` + +##### 解法二 + +还是对1到n进行遍历,对每个数统计该数1出现的次数,将每个数转换为字符串,判断字符串包含字符"1"的个数,但是将数字转换为字符串的这个过程,由于使用了StringBuffer的append()方法,然后使用了Integer的getChars方法,复杂度还是Log100 (N),所以总复杂度还是Nlog(N)。 + +##### 解法三 + +这种解法就是自己去找数学规律了 + +```java +public int NumberOf1Between1AndN_Solution(int n) { + int count = 0; + for (int i = 1; i <= n; i *= 10) { + int a = n / i,b = n % i; + //之所以补8,是因为当百位为0,则a/10==(a+8)/10, + //当百位>=2,补8会产生进位位,效果等同于(a/10+1) + count += (a + 8) / 10 * i + ((a % 10 == 1) ? b + 1 : 0); + } + return count; +} +``` + +## 题031把数组排成最小的数 + +输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。 + +##### 解题思路 + +本题其实就是对数组中的元素进行排序,只不过元素之间比较的不是值大小,而是字典序的大小,是以AB和BA的大小来决定的,所以对A和B进行拼接成,AB和BA,通过字符串比较,判断AB和BA的大小,AB>BA。这里主体主要使用冒泡排序 + +```java +public String PrintMinNumber(int [] numbers) { + if(numbers.length==1) { + return new StringBuffer().append(numbers[0]).toString(); + } + StringBuffer stringBuffer = new StringBuffer(); + for (int i = numbers.length; i > 0; i--) { + for (int j = 1; j < i ; j++) { + if (compare(numbers[j-1], numbers[j])) {//numbers[j]更大,需要交换到后面去 + int temp = numbers[j]; + numbers[j] = numbers[j-1]; + numbers[j-1] = temp; + } + } + } + for (int i = 0; i < numbers.length; i++) { + stringBuffer.append(numbers[i]); + } + return stringBuffer.toString(); +} +//判断a和b的字典序的大小的秘诀是,拼接两个字符串ab,ba,判断两个字符串,前面的字符大小 +public Boolean compare(int a, int b) { + String first = new StringBuffer().append(a).append(b).toString(); + String second = new StringBuffer().append(b).append(a).toString(); + for (int i = 0; i < first.length(); i++) { + Character char1 = first.charAt(i); + Character char2 = second.charAt(i); + if (char1.equals(char2)) { + continue; + } else if (char1 > char2) { + return true; + } else { + return false; + } + } + return true; +} +``` + +## 题032返回第N个丑数 + +把只包含质因子2、3和5的数称作丑数。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。 + +第一个丑数是1,除了1以外,其他丑数都是2,3,5之间相乘得到的,也就是丑数的因子都是2,3,5, + +第一种解决方案就是从1开始对所有整数遍历,将每个数一直除以2,3,5,看能否除尽,能除尽代表是丑数,一直得到第N个丑数。 + +第二种解决方案就是用一个数组来存丑数,将2,3,5当前对应的最小丑数乘以2,3,5,取最小值,作为最新的丑数,一直计算到第N个丑数。 + +使用index2,index3,index5记录上一次乘了2,3,5的数的最小值 + +```java +public int GetUglyNumber_Solution(int index) { + if (index == 0) return 0; + int[] array = new int[index]; + array[0] = 1;//最小的丑数是1 + int index2 =0 ,index3 = 0, index5 = 0;//分别代表上一次乘了2,3,5的index + for (int i = 1; i< index;i++){ + int temp2 = array[index2]*2; + int temp3 = array[index3]*3; + int temp5 = array[index5]*5; + int minTemp = temp2 < temp3 ? temp2 : temp3; + minTemp = minTemp < temp5 ? minTemp : temp5; + if (temp2 == minTemp) { + index2++; + } + if (temp3 == minTemp) { + //可能存在一个丑数可以由多种丑数相乘得到, + // 例如6可以是2*2*2,也可以是2*3,所以这里的三个if需要分开判断赋值 + index3++; + } + if (temp5 == minTemp) { + index5++; + } + array[i] = minTemp; + } + return array[index-1]; +} +``` + + +## 题034 数组中的逆序对 + +在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 + +输入描述: 题目保证输入的数组中没有的相同的数字 + +数据范围: + + 对于%50的数据,size<=10^4 + + 对于%75的数据,size<=10^5 + + 对于%100的数据,size<=2*10^5 + +逆序对就是前面的数比后面的数大,就是一个逆序对,可以使用归并排序,每次组合并时,右边的组的数比左边的数小时,会出现逆序对,逆序对的个数为左边的组的数组元素个数。 + +举例:假设数组是[9,8 ,5,4 ,11,7,6,23] + +分成四组,分别是 + +A组9,8 + +B组5,4 + +C组 11,7 + +D组6,23 + +那么对于5来说,它的逆序对其实是等于9在B组内的逆序对,5大于5,也就是1,然后是5对于A组,C组和D组的逆序对,所以可以进行归并排序来进行分组,然后进行数组归并,每次合并时,右边的组的数比左边的数小时,会出现逆序对,逆序对的个数为左边的组的数组元素个数。 + +```java +public int InversePairs(int [] array) { + if (array==null||array.length==0) {return 0;} + int[] tempArray = new int[array.length]; + return mergeSort(array,0,array.length-1,tempArray); + } + + int mergeSort(int[] array,int start,int end,int[] tempArray) { + int count = 0; + if (start>=end) { + return 0; + } + int middle = start + (end - start)/2; + //对左半部分,右半部分进行分组,然后进行合并 + count += mergeSort(array,start,middle,tempArray); + count += mergeSort(array,middle+1,end,tempArray); + //进行合并 + int i = start; + int j = middle+1; + int currentIndex = start; + while (i<=middle&&j<=end) { + if (array[j]1000000007?count%1000000007:count; + tempArray[currentIndex++] = array[j++]; + } else { + tempArray[currentIndex++] = array[i++]; + } + } + while (i<=middle) { + tempArray[currentIndex++] = array[i++]; + } + while (j<=end) { + tempArray[currentIndex++] = array[j++]; + } + //将临时数组的值拷贝到原数组 + for (int k = start; k <= end; k++) { + array[k] = tempArray[k]; + } + return count; + } +``` + +## 题036 数字在排序数组中出现的次数 + +统计一个数字在排序数组中出现的次数。 + +正常的二分查找是这样的,找不到时就会返回-1,所以此题可以计算K+0.5应该插入的位置减去 K-0.5应该插入的位置,就得到K的个数了 + +```java + public int GetNumberOfK1(int [] array , int k) { + if (array==null||array.length==0) { + return 0; + } + return left_bound(array,k+0.5) - left_bound(array,k-0.5); + } +//查找左边界,也就是>=taeget的元素的最小下标 + int left_bound(int[] array, double target) { + if (array==null||array.length == 0) return -1; + int left = 0; + int right = array.length-1; // 注意 + while (left<=right) { + int middle = left + (right -left)/2; + if (array[middle] == target) { + right = middle-1; + } else if(array[middle] < target) { + left = middle+1; + } else if(array[middle]>target) { + right = middle-1; + } + } +// if (left>=array.length) { +// return -1; +// } + return left; + } +``` + +## 题039 数组中只出现一次的数 + +一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 + +数组中有两个数字只出现了一次,其他数字都出现了两次, + +因为A异或A的结果是0,所以对数组遍历异或后的结果result,出现两次的数字异或结果为0, 所以result其实是两个只出现一次的数字B和C的异或结果,并且因为B,C不相等,所以result肯定也不等于0,result肯定有一位是1,在这一位上,肯定B,C中一个为1,一个为0,所以可以根据这一位将数组分成两个子数组,这样每个子数组只会包含一些出现过两次的数字和B,C中的一个,所以对两个子数组异或只会的结果就可以得到B和C。 + +```java + + public void FindNumsAppearOnce2(int [] array,int num1[] , int num2[]) { + if (array == null || array.length<=1) { + return; + } + int result = 0; + for (int i = 0; i < array.length; i++) { + //由于相同的数异或后会变成0,result其实是只出现一次的元素a和元素b的异或结果a^b + result = result ^ array[i]; + } + //bit通过跟a,b的异或结果result进行&元素,得到a,b不相同的第一个位置bit位 + int bit = 1; + while (bit<=result) { + if ((result&bit) > 0) { + break; + } else{ + bit = bit<<1; + } + } + int a=0; + int b=0; + //在bit位进行分组,bit位为1的为一组,一起异或,bit位为0的为一组,一起异或,其中a,b肯定是在不同组 + for (int i = 0; i < array.length; i++) { + if ((array[i]&bit)==0) { + a = a^array[i]; + } else { + b = b^array[i]; + } + } + num1[0] = a; + num2[0] = b; + } + +``` + +## 题040 和为S的连续正数序列 + +输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序。 + +##### 滑动窗口解法 + +就是使用滑动窗口来实现,当两个下标相差1时,计算的和还比sum大,这个时候会进行low++,会使得low==high,跳出循环,例如sum是100,那么在low=high=51时跳出循环 + +```java +public ArrayList> FindContinuousSequence(int sum) { + ArrayList arrayList = new ArrayList>(); + int low=1,high=2; + while (low(); + for (int k =low;k<=high;k++) { + tempArrayList.add(k); + } + arrayList.add(tempArrayList); + low++; + } else if (currentSum FindNumbersWithSum(int [] array, int sum) { + ArrayList arrayList = new ArrayList(); + int low = 0; + int high = array.length-1; + + while (low=1 && numbers[i]!=0&&numbers[i-1]!=0) { + if (numbers[i] == numbers[i-1]) { + return false; + } + needKing += numbers[i] - numbers[i-1]-1; + } + } + if (needKing>count) { + return false; + } + return true; +} + +//快排写法 +public static void qsort(int[] array, int start ,int end) { + if (start>=end) {return;} + int threhold = array[start];//阀值 + int i = start; + int j = end; + while (i < j) { + while (array[j] >= threhold && i < j) j--;//从右端找的应该需要写在前面 + while (array[i] <= threhold && i < j) i++; + int temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + //将现在最中间的数与最左端的数(也就是阀值)交换 + array[start] = array[i]; + array[i] = threhold; + qsort(array, start, i - 1); + qsort(array, i + 1, end); + +} +``` + +## 题049 数组中重复的数字 + +在一个长度为n的数组里的所有数字都在0到 n-1 的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。 + +解法一就是在遍历时将每个元素添加到hashSet,通过判断hashset中是否包含当前元素,来判断是否重复,由于这个长度为n数组中元素的取值范围是0-n-1,所以可以使用一个长度为n的数组array来代替hashSet记录元素是否出现,例如x出现了,将数组array[x]设置为1。 + +解法二就是将当前数组作为标记数组,每次遍历到下标为i的元素时,将array[array[i]]与当前元素交换,并且将array[array[i]]设置为-1,代表已经这个元素是重复元素,然后i- -,继续遍历交换后的这个元素。 + +```java +public boolean duplicate(int numbers[],int length,int [] duplication) { + if (numbers == null || numbers.length==0) { + return false; + } + for (int i=0;i=0; i--) { + result2 = result2 * A[i+1]; + d[i] = result2; + } + for (int i = 0; i < b.length; i++) { + b[i] = c[i]*d[i]; + } + return b; +} +``` + +## 题062 数据流的中位数 + +如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用 `Insert()` 方法读取数据流,使用 `GetMedian()` 方法获取当前读取数据的中位数。 + +可以使用插入排序来实现,就是每次插入元素,将元素与排好序的元素逐一比较,插入已排序后的数组,返回中位数时直接根据下标来获取 + +```java + ArrayList arrayList = new ArrayList(); + + public void Insert(Integer num) { + if (arrayList.size()==0) { + arrayList.add(num); + } else { + for (int i = arrayList.size()-1; i >=0 ; i--) { + if (arrayList.get(i)=num && i==0) {//插入的元素比数组中所有元素都小的情况 + arrayList.add(0,num); + } + } + } + } + + public Double GetMedian() { + if (arrayList.size()%2==1) { + return arrayList.get(arrayList.size()/2).doubleValue() ; + } else { + return (arrayList.get(arrayList.size()/2-1).doubleValue() + arrayList.get(arrayList.size()/2).doubleValue())/2; + } + } +``` + +## 题063 滑动窗口的最大值 + +给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组`{2,3,4,2,6,2,5,1}`及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为`{4,4,6,6,6,5}`; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: `{[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}`。 + +解题思路 + +思想主要是插入排序的思想,就是遍历数组,每次往双端队列后面插入元素,并且可以把比队尾比当前元素小的数都移除掉。插入到特定位置,队列是按顺序排序的,如果队头元素的下标超出了滑动窗口,就可以把它移除,一直到队头元素没有过期,这样就是这个此时的最大元素了。 + +其实插入排序的复杂度是O(N^2),因为需要对N个元素插入到排序好的序列,而每次插入的复杂度都是N,本题由于从队列尾部进行插入会将那些比较小的数移除掉,所以每次插入的时间复杂度是一个常数,总时间复杂度是O(N), + +具体步骤: + +##### 添加初始元素 + +1.遍历数组中的每个元素,如果队列为空,直接添加元素(考虑到有时候窗口大小是1和数组长度也是1,所以此时不能调用continue结束本次循环) + +##### 移除队尾比当前值小的元素 + +2.将当前元素num[i]从队列尾部进行插入,将那些比num[i]小的元素移除掉,因为num[i]的下标比它们大,值也比它们大,它们不可能成为最大值了 + +##### 添加当前值到队列尾部 + +3.比当前值小的数都从队尾移除完之后,再将当前值添加到队列尾部 + +##### 移除队首过期元素,添加当前窗口最大值到数组 + +4.计算滑动窗口左下标windowLeft,对队列头部元素下标进行判断,如果小于窗口左下标,说明过期了,需要移除,判断windowLeft是否>=0,满足说明才到计算滑动窗口最大值的时机,才会添加当前队列最大值到数组。 + +```java +public ArrayList maxInWindows2(int[] num, int size) { + ArrayList arrayList = new ArrayList<>(); + if (num==null||num.length==0||num.length queue = new LinkedList(); + for (int i = 0; i < num.length; i++) { + int currentValue = num[i]; + if (queue.size()==0) {//1.添加第一个元素 + queue.add(i); + } + //2.当前元素比队尾元素大,移除队尾元素 + while (queue.size() > 0 &¤tValue >= num[queue.getLast()]) { + queue.removeLast(); + } + queue.add(i); + int windowLeft = i - size + 1;//滑动窗口最左边的下标 + //3.顶部移除过期队列头部元素 + if (windowLeft>queue.getFirst()) { + queue.removeFirst(); + } + if (windowLeft>=0) {//进入第一个滑动窗口 + arrayList.add(num[queue.getFirst()]); + } + } + return arrayList; + } +``` + +## 题064 矩阵中的路径 + +请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 `a b c e s f c s a d e e` 这样的 `3 X 4` 矩阵中包含一条字符串`"bcced"`的路径,但是矩阵中不包含`"abcb"`路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。 + +判断一个字符串在字符矩阵中是否一条符号条件的路径 + +就是递归去判断就行了。 + +```java +public boolean hasPath(char[] matrix, int rows, int cols, char[] str) +{ + boolean[] flag = new boolean[matrix.length]; + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (matrix[i*cols + j] == str[0]) { + if (judge(matrix,rows,cols,i, j, str, 0, flag)) { + return true; + } + } + } + } + return false; +} + +//flag 标识这个节点是否已经在路径中了 + + +boolean judge(char[] matrix, int rows, int cols, int i, int j, char[] str, int charIndex,boolean[] flag) { + + + int index = i*cols + j; + if (i<0 || j<0 || i>=rows || j>=cols || matrix[index] != str[charIndex] || flag[index] == true) { + return false; + } + if (charIndex==str.length-1) { return true;} + + flag[index]=true; + if (judge(matrix,rows,cols,i+1, j, str, charIndex+1, flag) + ||judge(matrix,rows,cols,i-1, j, str, charIndex+1, flag) + ||judge(matrix,rows,cols,i, j+1, str, charIndex+1, flag) + ||judge(matrix,rows,cols,i, j-1, str, charIndex+1, flag)) { + return true; + } + + flag[index]=false; + return false; +} +``` diff --git a/docs/Coding_BinaryTree.md b/docs/Coding_BinaryTree.md new file mode 100644 index 0000000..357f0c1 --- /dev/null +++ b/docs/Coding_BinaryTree.md @@ -0,0 +1,765 @@ + +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +## 二叉树专题 +### 剑指Offer部分 +#### [题006重建二叉树](#题006) +#### [题016判断一个二叉树是否是另一个二叉树的子结构](#题016) +#### [题017二叉树的镜像](#题017) +#### [题021 从上往下打印二叉树](#题021) +#### [题022 判断是否是二叉搜索树的后序遍历](#题022) + +#### [题023 二叉树中和为某一值的路径](#题023) +#### [题025 二叉搜索树与双向链表](#题025) + +#### [题037 二叉树的深度](#题037) +#### [题038 判断是否是平衡二叉树](#题038) +#### [题056 二叉树的下一个节点](#题056) +#### [题057 对称的二叉树](#题057) +#### [题058 按之字形顺序打印二叉树](#题058) + +#### [题059 把二叉树打印成多行](#题059) +#### [题060序列化二叉树](#题060) +#### [题061 二叉搜索树的第K小的节点](#题061) + +## 题006重建二叉树 + +输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。 + +![image-20201206164927849](../static/image-20201206164927849.png) + +前序遍历结果和中序遍历结果: + +![image-20200624204944577](../static/image-20200624204944577.png) + +前序遍历结果分布是二叉树根节点,左子树,右子树。 + +中序遍历结果分布是左子树,二叉树根节点,右子树。 + +所以根据前序遍历结果的第一个元素获取到根节点,然后根据根节点把中序遍历结果分为两半,得到左子树的中序遍历结果,然后根据左子树的长度可以去前序遍历结果中分离出左子树的前序遍历结果,右子树也是如此,所以可以递归得构造出整个二叉树。 + +```java + public static TreeNode reConstructBinaryTree(int[] pre, int[] in) { + return reConstructBinaryTree(pre, 0, pre.length-1, in, 0, in.length-1); + } + + public static TreeNode reConstructBinaryTree(int[] pre, int preStart, int preEnd, int[] in, int inStart, int inEnd) { + if (preStart > preEnd || inStart > inEnd) { + return null; + } + TreeNode treeNode = new TreeNode(pre[preStart]); + for (int i = inStart; i <= inEnd; i++) { + if (in[i] == pre[preStart]) { + int leftLength = i - inStart;//左子树长度 + treeNode.left = reConstructBinaryTree(pre, preStart + 1, preStart+leftLength, in, inStart, i-1); + treeNode.right = reConstructBinaryTree(pre, preStart +leftLength+1, preEnd, in, i+1, inEnd); + } + } + return treeNode; + } +``` + +## 题016 判断一个二叉树是否是另一个二叉树的子结构 + +输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) + +有一个root为空,就返回false,然后判断根节点是否相等, + +* 相等,那么对根节点进行递归,判断子树根节点是否为NULL,是返回true,判断父树根节点是否为NULL,是返回false,然后对左右节点进行判断 +* 不相等,直接对左子树递归调用判断,是false,继续对右子树进行判断。 + +```java +//判断当前root2是否是root1及其子树的子结构 +public boolean HasSubtree(TreeNode root1,TreeNode root2) { + boolean result = false; + if (root1 != null && root2 != null) { + if (root1.val == root2.val) { + result = judgeTheTree(root1, root2); + //如果在这里就直接return result就会跳过对后面左右子树的判断 + } + if (result == false) { + result = HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2); + } + } + return result; +} +//判断 +boolean judgeTheTree(TreeNode root1, TreeNode root2) { + if (root2 == null) {return true;} + if (root1 == null) {return false;} + if (root1.val == root2.val) { + return judgeTheTree(root1.left, root2.left) && judgeTheTree(root1.right, root2.right); + } + return false; +} +``` + + + +## 题017 二叉树的镜像 + +操作给定的二叉树,将其变换为源二叉树的镜像。 + +就是翻转二叉树,将二叉树的左右子树进行交换。 + +```java +public void Mirror(TreeNode root) { + if (root != null) { + TreeNode tempNode = root.left; + root.left = root.right; + root.right = tempNode; + Mirror(root.left); + Mirror(root.right); + } +} +``` + +## 题021 从上往下打印二叉树 + +从上往下打印出二叉树的每个节点,同层节点从左至右打印。 + +##### 宽度优先遍历 + +其实就是二叉树的宽度优先遍历,一般就是通过队列来实现,将根节点添加到队列中,然后对队列进行循环,每次从队列取出一个元素,添加到ArrayList中去,然后将左,右子节点添加到队列中去,然后继续循环,一直到队列中取不到元素。(Java中可以使用LinkedList的add(),remove()方法来实现队列。 + +```java +public ArrayList PrintFromTopToBottom(TreeNode root) { + ArrayList arrayList = new ArrayList(); + if (root==null) { + return arrayList; + } + //这里用LinkedList比较好,因为它的底层是基于链表实现的,在队列取出队头元素时,不后面的元素不需要前移。 + LinkedList queue = new LinkedList(); + queue.add(root); + while (queue.size()>0) { + TreeNode treeNode = queue.remove(0); + arrayList.add(treeNode.val); + if (treeNode.left!=null) queue.add(treeNode.left); + if (treeNode.right!=null) queue.add(treeNode.right); + } + return arrayList; +} +``` + +##### 深度优先遍历 + +一般是使用栈来实现,一开始将 + +1.根节点加入栈, + +2.将栈顶元素出栈,打印这个节点,然后将它的右子节点入栈,将其左节点入栈 + +3.重复2操作,一直到栈中元素为空。 + +也可以使用递归实现 + +```java +//使用递归实现深度遍历 +ArrayList list = new ArrayList(); +void deepTranverse(TreeNode node) { + if(node!=null) { + list.add(node); + deepTranverse(node.left); + deepTranverse(node.right); + } +} +//使用栈的实现深度遍历 +void deepTranverse(TreeNode node) { + Stack stack=new Stack(); + List list=new ArrayList(); + if(root==null) + return list; + //压入根节点 + stack.push(root); + //然后就循环取出和压入节点,直到栈为空,结束循环 + while (!stack.isEmpty()){ + TreeNode t=stack.pop(); + if(t.right!=null) + stack.push(t.right); + if(t.left!=null) + stack.push(t.left); + list.add(t.val); + } + return list; +} +``` + +## 题022 判断是否是二叉搜索树的后序遍历 + +输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出 Yes ,否则输出 No 。假设输入的数组的任意两个数字都互不相同。 + +![image-20201205191448743](../static/image-20201205191448743.png) + +二叉搜索树的特点在于,左子树所有节点<根节点<右子树所有节点,后续遍历的遍历顺序是左子树,右子树,根节点,所以取出数组最后一个元素,也就是根节点,然后遍历序列,发现第一个比根节点大的数之后,这个是也就是临界点,后面的数肯定也需要比根节点大,否则不是后续遍历,遍历完成后。 + +* 没有临界点(也就是第一个右子树的节点),全部都是左子树,递归调用判断, +* 临界点等于第一个元素,全部都是右子树,递归调用判断, +* 其他情况,将序列分为左子树,右子树,递归调用判断, + +(遍历时如果只有一个元素,那么直接返回正确,肯定满足要求) + +```java +public static boolean VerifySquenceOfBST(int [] sequence) { + if (sequence == null || sequence.length ==0) { + return false; + } + return VerifySquenceOfBST(sequence,0,sequence.length-1); +} + +public static boolean VerifySquenceOfBST(int[] sequence, int start, int end) { + if (start==end) { + return true; + } + Integer rightChildIndex = null; + for (int i = start;i sequence[end]&&rightChildIndex==null) { + rightChildIndex = i; + } + if (rightChildIndex!=null&&sequence[i] < sequence[end]) {//右子树有更小的元素 + return false; + } + } + if(rightChildIndex==null) {//说明全部位于左子树 + return VerifySquenceOfBST(sequence,start,end-1); + } else if(rightChildIndex== start) {//说明全部位于右边子树 + return VerifySquenceOfBST(sequence,start,end-1); + } + return VerifySquenceOfBST(sequence,start,rightChildIndex-1) && VerifySquenceOfBST(sequence,rightChildIndex, end-1); +} +``` + +## 题023 二叉树中和为某一值的路径 + +输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的 list 中,数组长度大的数组靠前) + +就是递归调用每个节点的左右子树,然后将节点值相加,如果节点值和为某个预期值,并且该节点为叶子节点,那么这条路径就是要找的路径。 + +```java +public ArrayList> FindPath(TreeNode root, int target) { + ArrayList> arrayContainer = new ArrayList>(); + if (root == null) { + return arrayContainer; + } + judgeIfIsTarget(arrayContainer, new ArrayList(),root,target,0); + return arrayContainer; +} + +public void judgeIfIsTarget(ArrayList> arrayContainer, ArrayList currentArrayList, TreeNode root, int target,int sum) { + if (root == null) {//根节点为空,直接结束这条路径的遍历 + return ; + } + currentArrayList.add(root.val); + sum = sum+root.val; + if (sum == target && root.left == null && root.right == null) {//当前路径满足要求就将路径添加到数组中去 + ArrayList copyArrayList = new ArrayList<>(currentArrayList); + arrayContainer.add(copyArrayList); + } else { + //判断左边节点的路径是否满足需求 + judgeIfIsTarget(arrayContainer, new ArrayList<>(currentArrayList), root.left, target,sum); + //判断右边界的路径是否满足需求 + judgeIfIsTarget(arrayContainer, new ArrayList<>(currentArrayList), root.right, target,sum); + } +} +``` + +另外一种写法 + +```java +public ArrayList> FindPath(TreeNode root, int target) { + ArrayList> allLists = new ArrayList>(); + if (root==null) { + return allLists; + } + ArrayList currentArray = new ArrayList(); + judgeIfIsTarget(allLists,root,target,currentArray,0); + return allLists; + } +public void judgeIfIsTarget(ArrayList> allList,TreeNode root, int target,ArrayListcurrentArray, int currentValue) { + currentValue += root.val; + if (currentValue > target) {//大于目标值,这条路径不可能是了 + return; + } else if (currentValue == target && root.left == null && root.right == null) {//必须要是根节点 + currentArray.add(root.val); + ArrayList newArray = new ArrayList<>(currentArray); + allList.add(newArray); + } else {//小于target + currentArray.add(root.val); + ArrayList newArray = new ArrayList<>(currentArray); + if (root.left!=null) {//遍历左子树 + judgeIfIsTarget(allList,root.left,target,newArray,currentValue); + } + if (root.right!=null) {//遍历右子树 + judgeIfIsTarget(allList,root.right,target,newArray,currentValue); + } + } + } +``` + + + +## 题025 二叉搜索树与双向链表 + +输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 + +二叉搜索树的中序遍历的结果就是递增的序列,所以递归实现二叉搜索树的中序遍历 + +```java + TreeNode head = null;//主要记录双向链表头结点 + TreeNode lastNode = null;//主要记录中序遍历时,上一次遍历的节点 + public TreeNode Convert(TreeNode pRootOfTree) { + if (pRootOfTree == null) { + return null; + } + Convert(pRootOfTree.left); + if (head == null) {//这里相当于是把第一次执行Convert方法的元素设置为链表头结点,也就是中序遍历第一个位置的节点,也就最左边的叶子节点。 + head = pRootOfTree; + } + if (lastNode != null) {//中序遍历时,假设存在上一个遍历的节点,将上一个节点与这个节点进行关联 + lastNode.right = pRootOfTree; + pRootOfTree.left = lastNode; + } + //完成对当前节点的遍历,将当前设置为lastNode。 + lastNode = pRootOfTree; + Convert(pRootOfTree.right); + return head; + } +``` + + +## 题037 二叉树的深度 + +输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 + +递归来遍历就好了 + +```java + public int TreeDepth(TreeNode root) { + if (root==null) { + return 0; + } + int left = TreeDepth(root.left); + int right = TreeDepth(root.right); + return left > right ? left+1 : right+1; + } +``` + +## 题038 判断是否是平衡二叉树 + +平衡二叉树的特点就是**任意节点的左右子树高度差的绝对值都小于等于1**, + +也可以先根据上面计算二叉树深度的算法先计算左右高度差,然后再去减,判断当前节点是否满足平衡二叉树的要求,然后再去对左子节点,和右子节点做同样的操作,但是这样的问题在于会对节点多次重复遍历。如果是把顺序调换一下,先去分别计算左右子节点的最大高度,过程中,发现不符合平衡二叉树的要求时,直接返回-1,这样就直接结束了,否则返回最大高度。 + +```java +public boolean IsBalanced_Solution1(TreeNode root) { + if (root==null) { + return true; + } + return fetchDepthIfSatisfy(root) != -1;//如果正常返回深度,则是平衡的,否则根节点子树中存在不平衡 + } + +//如果满足条件就返回当前节点的深度,当子树中存在节点不符合平衡二叉树会返回-1 +Integer fetchDepthIfSatisfy(TreeNode root) { + if (root == null) { + return 0; + } + Integer leftDepth = fetchDepthIfSatisfy(root.left); + Integer rightDepth = fetchDepthIfSatisfy(root.right); + if (leftDepth==-1 || rightDepth==-1) {//代表子树中有节点不满足条件 + return -1; + } + if (leftDepth - rightDepth > 1 || leftDepth - rightDepth < -1) {//代表当前节点不满足条件 + return -1; + } + return leftDepth> rightDepth? leftDepth+1:rightDepth+1; + } + +``` + +## 题056 二叉树的下一个节点 + +给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。 + +主要就是分情况讨论 + +中序遍历就是左子树,根节点,右子树 + +所以当前节点的中序遍历中的下一个节点是 + +1.有右子树 + +​ 1.1右子树有左节点,一直向下遍历,找到最左的叶子节点。 + +​ 1.2右子树没有左节点,就是右子树节点。 + +2.没有右子树 + +​ 2.1没有父节点,那么没有下一个节点。 + + 2.2有父节点 + +​ 2.3当前节点是父节点的左子树,直接返回父节点。 + +​ 2.4当前节点是父节点的右子树,一直向上遍历,直到找到一个父节点,他是祖先节点的左子树节点的,找到就返回祖先节点,找不到就返回空。 + +```java +public TreeLinkNode GetNext(TreeLinkNode pNode) +{ + //这个节点有右子树 + if (pNode.right != null) { + TreeLinkNode right = pNode.right; + if (right.left==null) {//右子树没有左节点 + return right; + } else { + TreeLinkNode leftNode = right.left; + while (leftNode.left!= null) {//右子树有左节点 + leftNode = leftNode.left; + } + return leftNode; + } + } else {//这个节点没有右子树,那么就去找父节点 + TreeLinkNode father = pNode.next; + if (father == null) {//父节点为空 + return null; + } else if(father.left == pNode) {//父节点不为空,该节点为父节点的左子树 + return father; + } else {//父节点不为空,该节点为父节点的右子树 + while (father.next!=null) { + TreeLinkNode grandFather = father.next; + if (grandFather.left == father) { + return grandFather; + } else { + father = grandFather; + continue; + } + } + } + } + return null; +} +``` + +## 题057 对称的二叉树 + +请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。 + + + +前序遍历是根节点,左子树,右子树 + +假设有另外一种遍历是根节点,右子树,左子树,如果二叉树是对称,那么这两种遍历的结果是一样的,所以使用递归来进行两种遍历,然后在过程中判断两种遍历结果是否一样。 + +```java +boolean isSymmetrical(TreeNode pRoot) +{ + return isSymmetrical(pRoot,pRoot); +} + +boolean isSymmetrical(TreeNode leftRoot,TreeNode rightRoot) +{ + + if (leftRoot == null || rightRoot == null) { + return (leftRoot == null) & (rightRoot == null); + } + if (leftRoot.val!=rightRoot.val) { + return false; + } else { + return isSymmetrical(leftRoot.left, rightRoot.right) + & isSymmetrical(leftRoot.right, rightRoot.left); + } +} +``` + +##### 另外一种写法 + +```java +boolean isSymmetrical1(TreeNode pRoot) { + if (pRoot == null) { + return true; + } + return isSymmetrical1(pRoot.left,pRoot.right); +} +boolean isSymmetrical1(TreeNode leftNode,TreeNode rightNode) { + if (leftNode==null && rightNode==null) {//都为null + return true; + } + if (leftNode==null && rightNode!=null) {//其中一个为null + return false; + } + if (leftNode!=null && rightNode==null) {//其中一个为null + return false; + } + if (leftNode.val==rightNode.val) {//都不为null,判断左右节点是否对称 + return isSymmetrical1(leftNode.left,rightNode.right) && isSymmetrical1(leftNode.right,rightNode.left); + } + return false; +} +``` + + + +## 题058 按之字形顺序打印二叉树 + +就是使用两个栈,stack1存放奇数层的节点,stack2存放偶数层的节点,一开始将根节点加入奇数层的栈,开始遍历, + +当前处于奇数层时,每次对stack1出栈,将出栈的节点的值打印,然后依次将节点的左子节点,右子节点加入到stack2,一直到stack1的全部元素出栈。 + +当前出于偶数层时,每次对stack2出栈,将出栈的节点的值打印,然后依次将节点的右子节点,左子节点加入大屏stack2,一直到stack2的全部元素出栈。 + +```java +public ArrayList> Print(TreeNode pRoot) { + ArrayList> arrayLists = new ArrayList>(); + if (pRoot==null) return arrayLists; + Stack stack1 = new Stack();//存放奇数层的栈 + Stack stack2 = new Stack();//存放偶数层的栈 + int flag = 0;//代表当前遍历的是奇数层还是偶数层。区别在于添加子节点的顺序。 + stack1.add(pRoot); + while ((flag == 0 && stack1.size()>0) || (flag == 1 && stack2.size()>0)) { + if (flag==0) { + ArrayList array = new ArrayList(); + while (stack1.size()>0) { + TreeNode node = stack1.pop(); + array.add(node.val); + //flag0是奇数层,子节点从左往右添加到栈中, + if (node.left!=null) stack2.push(node.left); + if (node.right!=null) stack2.push(node.right); + } + arrayLists.add(array); + flag = 1; + } else { + ArrayList array = new ArrayList(); + while (stack2.size()>0) { + TreeNode node = stack2.pop(); + array.add(node.val); + //flag1是偶数层,子节点从右往左添加到栈中 + if (node.right!=null) stack1.push(node.right); + if (node.left!=null) stack1.push(node.left); + } + arrayLists.add(array); + flag = 0; + } + } + return arrayLists; +} +``` +这是另外一种写法。 +```java +public ArrayList> Print(TreeNode pRoot) { + ArrayList> list = new ArrayList>(); + Stack otherStack = new Stack(); + Stack currentStack = new Stack(); + if(pRoot == null) { + return list; + } + currentStack.push(pRoot); + int addChildFromRightFlag = 0;// + while(currentStack.size()>0 || otherStack.size()>0) { + ArrayList array = new ArrayList(); + while(currentStack.size()>0) { + TreeNode node = currentStack.pop(); + array.add(node.val); + if(addChildFromRightFlag == 0) {//根据层数的不同,决定从右边还是左边添加节点。 + if(node.left!=null) { + otherStack.add(node.left); + } + if(node.right!=null) { + otherStack.add(node.right); + } + } else { + if(node.right!=null) { + otherStack.add(node.right); + } + if(node.left!=null) { + otherStack.add(node.left); + } + } + } + list.add(array); + addChildFromRightFlag = addChildFromRightFlag == 0 ? 1:0; + currentStack = otherStack; + otherStack = new Stack(); + } + return list; + } +``` + + + +## 题059 把二叉树打印成多行 + +宽度优先遍历的话,就是使用一个队列来实现,这里需要每一层的节点在一行打印,其实就是宽度优先遍历时需要区分每一层。 + +##### 使用两个队列的解法 + +可以使用两个队列,队列queue1存放奇数层节点,队列queue2存放偶数层节点,一开始将根节点加到队列queue1,然后对queue1所有元素按顺序出列,每次将元素的左右子节点添加到偶数队列queue2中,知道queue1队列元素全部出列,然后对queue2队列重复queue1的操作,直到queue1,queue2的元素都为空。 + +##### 使用一个队列的解法(使用空节点分割) + +就是使用一个队列queue,一开始将根节点加入queue,并且加入一个null元素到队列中作为标志元素,用来分割每一层,标志这一层的节点都在标志元素的前面。然后对queue中元素出列,每个进行打印,直到出列的元素是null,表示这一层已经结束了,如果queue中还有元素,那么在后面加入null标志元素分割,并且进行换行,打印下一行,如果queue中没有元素就结束循环 + +```java +ArrayList> Print(TreeNode pRoot) { + ArrayList> arrayLists= new ArrayList>(); + if (pRoot == null) return arrayLists; + ArrayList queue = new ArrayList<>(); + queue.add(pRoot); + queue.add(null);//每一层结束时,添加标志节点 + ArrayList tempArrayList = new ArrayList<>(); + while (queue.size()>0) { + TreeNode treeNode = queue.remove(0); + if (treeNode==null) {//null是标志节点,说明这一层已经打印结束了 + arrayLists.add(tempArrayList); + tempArrayList = new ArrayList<>(); + if (queue.size() == 0) {break;}//如果队列里没有元素了,就结束循环 + else { queue.add(null); }//如果队列里还有元素就继续添加标志节点用于分割 + } else { + tempArrayList.add(treeNode.val); + if (treeNode.left!=null) queue.add(treeNode.left); + if (treeNode.right!=null) queue.add(treeNode.right); + } + } + return arrayLists; +} +``` + +递归的解法 + +就是递归遍历每一个节点,遍历时传入深度depth,将节点加入到ArrayList中特定深度对应的数组中去。(但需要注意的是,这种方式其实是深度遍历的先序遍历,所以在添加节点到数组中时的顺序不是某一层的节点添加完毕后,才添加下一层的,所以如果是需要当时打印的话就不行,这种方法其实是将某一层的节点添加到专门存这一层的数组中,后续全部遍历完毕后,打印每个数组) + +这种方法也可以用来进行二叉树深度遍历,遍历完之后将嵌套数组拆分成单层的数组。 + +```java +ArrayList> Print2(TreeNode pRoot) { + ArrayList> arrayLists = new ArrayList>(); + if (pRoot==null)return arrayLists; + find(pRoot,1,arrayLists); + return arrayLists; +} + +void find(TreeNode pRoot, int depth, ArrayList> arrayLists) { + if (arrayLists.size()< depth) {//还没有存放这一层节点的数组 + arrayLists.add(new ArrayList()); + } + ArrayList tempArrayList = arrayLists.get(depth-1); + tempArrayList.add(pRoot.val); + if (pRoot.left!=null) find(pRoot.left,depth+1,arrayLists); + if (pRoot.right!=null) find(pRoot.right,depth+1,arrayLists); +} +``` + +## 题060序列化二叉树 + +请实现两个函数,分别用来序列化和反序列化二叉树 + + + +序列化的过程就是二叉树宽度遍历的过程,null元素也会加入到序列化的字符串中去 + +反序列化就是调用String.split()方法 + +1.将字符串先转换为String[]数组的过程, + +2.然后也是去数组取出第一个字符串firstStr,创建一个根节点treeNode,将firstStr转换为Integer类型,赋值给treeNode,然后将treeNode添加到一个队列中去。 + +3.每次从队列取出一个元素node,依次从String[]数组中取两个值,转换为Intger类型,作为node的左,右子节点,如果左,右子节点的值不为null,那么就将左右子节点添加到队列中去。 + +4.重复步骤3,直到队列元素个数为空。 + +```java +String Serialize(TreeNode root) { + StringBuffer stringBuffer = new StringBuffer(); + if (root == null) {return stringBuffer.toString();} + ArrayList queue = new ArrayList(); + queue.add(root); + while (queue.size()>0) { + TreeNode node = queue.remove(0); + if (node == null) { + stringBuffer.append("#!"); + } else { + stringBuffer.append(node.val+"!"); + queue.add(node.left); + queue.add(node.right); + } + } + return stringBuffer.toString(); +} + +TreeNode Deserialize(String str) { + if (str == null || str.length() == 0) {return null;} + String[] array = str.split("!"); + + Integer rootValue = convert(array[0]); + if (rootValue == null) {return null;} + + TreeNode rootNode = new TreeNode(rootValue); + ArrayList queue = new ArrayList(); + queue.add(rootNode); + int currentIndex = 1; + while (queue.size()>0 && currentIndex左子树所有节点,根节点<右子树所有节点 + +由于前序遍历是先左子树,根节点,右子树的顺序,所以前序遍历的结果,就是二叉搜索树中元素按递增顺序排列的结果,所以按照前序遍历到第K个元素就是第K小的节点。 + +```java +Integer index = 0; +TreeNode kNode = null; + +TreeNode KthNode(TreeNode pRoot, int k) +{ + find(pRoot,k); + return kNode; +} + +void find(TreeNode node, Integer k) { + if (node == null) {return;} + find(node.left, k); + index++; + if (index == k) { + kNode = node; + } else { + find(node.right, k); + } +} +``` + + + diff --git a/docs/Coding_LinkedList.md b/docs/Coding_LinkedList.md new file mode 100644 index 0000000..18d95b3 --- /dev/null +++ b/docs/Coding_LinkedList.md @@ -0,0 +1,475 @@ + +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +## 链表专题 +一开始是刷剑指offer,后面是刷LeetCode,发现就是题目都是相对比较零散,不是按照链表,二叉树,数组等等这些Tag来分的,所以不是特别方便自己复习,所以自己在复习时就把刷过的题按照Tag重新刷了一遍。 + +### 剑指Offer部分 +#### [题005从尾到头打印链表](#题005) +#### [题013链表的倒数第K个结点](#题013) +#### [题014反转链表](#题014) +#### [题015 合并链表](#题015) + +#### [题024 复杂链表的复制](#题024) +#### [题035 两个的链表的第一个公共节点](#题035) + +#### [题054 链表中环的入口节点](#题054) +#### [题055 删除链表中重复的节点](#题055) + +## 题005 从尾到头打印链表 + +输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。 + +#### 解题思路(递归遍历链表) + +首先通过开始的判断,来排除链表为空的情况,直接返回空数组,链表不为空,取下一个节点,判断下一个节点是否为空, + +- 不为空,那么递归调用printListFromTailToHead方法来获取后面的节点反序生成的ArrayList,然后添加当前的节点的值,然后返回arrayList。 +- 为空,那么说明当前节点是链表尾部节点,直接创建一个ArrayList,然后添加当前节点的值,然后返回arrayList。 + +```java +ArrayList printListFromTailToHead(ListNode listNode) { + if(listNode == null) { return new ArrayList(); } + ArrayList arrayList; + ListNode nextNode = listNode.next; + if (nextNode!=null) { + arrayList = printListFromTailToHead(nextNode); + arrayList.add(listNode.val); + } else { + arrayList = new ArrayList<>(); + arrayList.add(listNode.val); + } + return arrayList; +} +``` +或者是这样写,其实原理就是先递归遍历,然后再打印,这样链表打印的顺序就是逆序的了。下面这么写,通过把ArrayList设置为一个全局变量,更加便于理解。 +```java +ArrayList list = new ArrayList(); +public ArrayList printListFromTailToHead(ListNode listNode) { + if(listNode == null ){ + return list; + } + printListFromTailToHead(listNode.next); + list.add(listNode.val); + return list; +} +``` + + +## 题013链表的倒数第K个结点 + +输入一个链表,输出该链表中倒数第k个结点。 + +#### 解题思路(快慢指针) +一个指针A先向前走k-1步,然后一个指针B指向头结点,A,B同时往后面走,直到A成为最后一个节点。 + +```java +ListNode FindKthToTail(ListNode head, int k) { + if (head==null || k <= 0) {//空链表,或者k小于等于0 + return null; + } + ListNode secondNode = head; + + for (int i=0 ; i < k-1 ; i++) {//向前走k-1步 + if (secondNode.next==null) {//链表长度不足k个 + return null; + } + secondNode = secondNode.next; + } + + ListNode firstNode = head; + while (secondNode.next != null) {//一直遍历到secondNode成为最后一个节点 + secondNode = secondNode.next; + firstNode = firstNode.next; + } + return firstNode; +} +``` +还有一种方法更加简洁一些,就是让快指针quickNode走到为null,此时head就是倒数第K个节点。 +```java +ListNode findLastK(ListNode head,int k) { + if (head==null || k < 1) {//空链表,或者k小于等于0 + return null; + } + ListNode quickNode = head; + for (int i = 0; i < k; i++) { + if (quickNode == null) { + return null; + } else { + quickNode = quickNode.next; + } + } + while (quickNode!=null) { + quickNode = quickNode.next; + head=head.next; + } + return head; + } +``` +## 题014反转链表 + +输入一个链表,反转链表后,输出新链表的表头。 + +A = head + +B = head.next + +head = null;//特别注意需要将原本的头结点置为null,否则原来的头结点的next会引用原来的第二个节点,形成一个环。 + +上一个节点A,当前节点B,下一个节点C,让 + +C = B.next; + +B.next = A; + +A = B; + +B = C; + +一直到B为null,此时A为最后一个节点. + +```java +public static ListNode ReverseList(ListNode head) { + + if (head == null) return null;//链表为空 + if (head.next==null) return head;//链表只有一个节点 + ListNode lastNode = head; + ListNode currentNode = head.next; + head.next = null;//将原来的头结点指向null + + while (currentNode != null) {//一直到currentNode是最好一个节点 + ListNode saveNextNode = currentNode.next; + currentNode.next = lastNode; + lastNode = currentNode; + currentNode = saveNextNode; + } + return lastNode; +} +``` + +这种解法好理解一点,就是使用first,second,three保存三个连续的节点,依次后移动 + +```java +public ListNode findLastNode(ListNode node) { + if(node==null ||node.next ==null) { + return node; + } + //至少有两个节点 + ListNode first = node; + ListNode second = node.next; + ListNode three = second.next; + first.next = null; + while(second!=null) { + second.next = first; + first = second; + second = three; + if (three == null){break}; + else { + three = three.next; + } + } + return first; +} +``` +还有一种是递归的写法 +```java +ListNode reverseNode(ListNode head) { + ListNode lastNode = reverseNode2(head); + head.next = null;//把老的链表头结点的next指针设置为null,不然会形成环 + return lastNode; + } + //递归遍历到末尾 +ListNode reverseNode2(ListNode head) { + if (head.next==null) { + return head; + } + ListNode lastNode = reverseNode2(head.next); + head.next.next = head; + return lastNode; +} + +``` + +## 题015 合并链表 + +输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。 + +遍历写法: + +```java + ListNode merge(ListNode head1, ListNode head2) { + //有一个链表为空,就将另一个链表返回 + if (head1==null) { return head2;} + if (head2==null) { return head2;} + //创建preNode作为新链表头结点前面的节点 + ListNode preHead = new ListNode(-1); + //currentNode作为起始节点 + ListNode currentNode = preHead; + while (head1!=null&&head2!=null) { + if (head1.vallength2) { + length1--; + node1=node1.next; + } else { + length2--; + node2=node2.next; + } + } + while (node1!=node2) { + node1 = node1.next; + node2 = node2.next; + } + if (node1==node2) { + return node1; + } + return null; + } +``` + +## 题054 链表中环的入口节点 + +给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。 + + +一种方法是遍历整个链表,将每个节点添加到HashSet中,判断是否在HashSet中出现过,第一个重复的节点就是环的入口节点。 + +另一种解决方法是,假设存在环,环的长度为x,第一个指针先走x步,然后第二个指针从链表头结点出发,两个指针一起走,当第而个指针刚好走到环入口时,第一个指针正好在环中走了一圈,也在环的入口,此时的节点就是环的的入口节点, + +怎么得到环的长度呢,就是一个指针每次走2步,一个指针每次走一步,他们相遇时的节点肯定就是在环中的某个节点,然后这个节点在环中遍历一圈,回到原点,就可以得到环的长度count。 + +两个指针从头出发,第一个指针先走count步,然后两个指针每次都只走一步,相遇的地方就是环的入口, + +``` +public ListNode EntryNodeOfLoop(ListNode pHead) +{ + if (pHead == null || pHead.next==null) { + return null; + } + //计算环的长度 + ListNode slowNode = pHead.next; + ListNode quickNode = slowNode.next; + ListNode nodeInLoop = null;//获取环上的某个节点 + while (quickNode!=null && slowNode!= null) { + if (quickNode == slowNode) { + nodeInLoop = quickNode; + break; + } + slowNode = slowNode.next; + quickNode = quickNode.next; + if (quickNode!= null) { + quickNode=quickNode.next; + } + } + if (nodeInLoop == null) {//说明没有环 + return null; + } + //根据环上的某个节点来计算环的长度count + ListNode tempNode = nodeInLoop; + int count = 1;//将当前计算计算在内 + while (tempNode.next!=nodeInLoop) { + tempNode = tempNode.next; + count++; + } + //从链表头结点出发,第一个指针先走count步,然后两个指针每次只走一步,相遇的地方就是环的入口, + // 然后第一个指针和第二个指针一起走,当第二个指针刚好走了x步到环入口时, + // 第一个指针正好走了x+count步,在环中走了一圈,也在环的入口, + quickNode = pHead; + for (int i = 0; i < count; i++) { + quickNode = quickNode.next; + } + + slowNode = pHead; + while (quickNode!=slowNode) { + quickNode = quickNode.next; + slowNode=slowNode.next; + } + return slowNode; +} +``` + +使用hashSet的解法 + +``` +public ListNode EntryNodeOfLoop1(ListNode pHead) +{ + if (pHead==null) { + return null; + } + HashSet set = new HashSet(); + ListNode node = pHead; + while (node !=null) { + if (set.contains(node)) { + return node; + } else { + set.add(node); + } + node= node.next; + } + return null; +} +``` + +## 题055 删除链表中重复的节点 + +在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5 + +解法: + +就先创建一个我们自己的节点ourHead, + +ourHead.next= head, + +pre = ourHead + +currentNode = pre.next + +然后currentNode开始向后遍历,每次拿当前节点与后一个节点值比较 + +相等,那么就遍历找到一个不相等的点,然后将pre节点指向这个不相等的节点,currentNode = pre.next + +不相等,那么就直接让pre和currentNode向后移动一步。 + +```java +public ListNode deleteDuplication(ListNode pHead) +{ + if (pHead == null || pHead.next == null) { + return pHead; + } + + ListNode ourHead = new ListNode(0); + ourHead.next = pHead; + int temp = pHead.val; + ListNode preNode = ourHead; + ListNode currentNode = ourHead.next; + + while (currentNode!=null) {//往后遍历 + if (currentNode.next!=null && currentNode.val == currentNode.next.val) {//如果当前节点与下一个节点相等,就找到一个与当前节点不相等的节点,然后把中间多出来的这些相等的节点都删除掉 + ListNode tempNode = currentNode.next; + //找到第一个不相等的节点 + while (tempNode!=null) { + if(tempNode.val == currentNode.val) { tempNode = tempNode.next; } + else { break; } + } + preNode.next = tempNode; + currentNode = preNode.next; + } else {//如果当前节点与下一个节点相等,就跳过,遍历下一个节点 + preNode = preNode.next; + currentNode = currentNode.next; + } + } + return ourHead.next; +} +``` \ No newline at end of file diff --git a/docs/Git.md b/docs/Git.md new file mode 100644 index 0000000..83ed882 --- /dev/null +++ b/docs/Git.md @@ -0,0 +1,126 @@ +##### git命令使用 + +`git branch dev` 创建一个名称为dev的分支 + +`git checkout dev`切换到dev分支上 + + +`git checkout -b dev ` 从当前分支上创建出一个新分支dev + + +`git merge dev` 假设当前为master分支,执行这条命令后会将dev分支合并到当前master分支,合并完成后,master分支和dev分支会变成同一个分支 + +`git:(dev) git rebase master` 假设当前为dev分支,git rebase master命令会将当dev的更改进行回退,回退到与main分支交汇的地方,将这些更改暂存到rebase目录,然后将master上面的提交拿过来,让dev分支更新到master分支那样,然后再把之前暂存的更改应用上,中间如果出现冲突,需要解决冲突,解决完冲突后,使用git add 将更新添加上,然后使用git rebase --continue继续合并。如果中间需要中断合并那么可以使用git rebase —abort。 + +在rebase完成后,我们dev分支已经是最新了,但是master上还是老的代码,我们需要使用git checkout master 切换到master分支上,然后使用git rebase dev将dev上面的更改移动到master上来,然后push到远程。 +![image-20210603172535748](../static/image-20210603172535748.png) + +`git checkout HEAD^` 将当前head指向上1次提交 + +`git checkout HEAD~3` 将当前head指向上3次提交 + +`git reset HEAD~1` 回滚到上一次提交 + +`git reset HEAD` 生成一个新的提交,用于将上一个提交的更改撤销掉,等价于`git reset HEAD~1` + +`git cherry-pick C3 C4 C7`将其他分支上的C3提交,C4提交,C7提交拿到这个分支上面来 + +`git rebase -i HEAD~3` 合并最近的两次提交为一次提交记录,执行这个命令后会进入vim界面,然后会出现3次提交的记录 + +```java +pick 115e825 queryCreditsMonth +pick 4cedfe6 queryCreditsMonth +pick b3dccfd nickname +``` + +有一下操作符: +pick:保留该commit(缩写:p) +reword:保留该commit,但我需要修改该commit的注释(缩写:r) +edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e) +squash:将该commit和前一个commit合并(缩写:s) +fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f) +exec:执行shell命令(缩写:x) +drop:我要丢弃该commit(缩写:d) + +咱们如果要把三次提交合并成一个就改成这样 + +``` +pick 115e825 queryCreditsMonth +s 4cedfe6 queryCreditsMonth +s b3dccfd nickname +``` + +然后保存退出,然后会进入填写commit说明的地方,我们直接保存就好了,这样就完成了,会生成一个新的commit + +```java +commit b3dccfd2c2173fa0a6358de604b6541c8c6c644a (HEAD -> feature-dev) +Date: Fri May 7 16:29:22 2021 +0800 + + nickname + + nickname + + change credits +``` + + + +`git commit -amend`可以修改最后一次提交的commit说明 + + + + + + + +##### rebase出现了冲突怎么办? + +假设master上跟我们改了同一行,其实就会造成冲突 +例如下面这个就是我们需要解决的冲突,<<<<< HEAD 到====== 之间为其他分支上别人的更改,======到>>>>>>> change credits之间为我们自己在dev分支上的更改,我们需要解决冲突,然后使用git add对文件进行提交 +```java + @Override + public List creditsMonthsQuery(Integer months, long year) { +<<<<<<< HEAD + List creditsMonth = creditsMonthsMapper.creditsMonthsQuery(months, year); + for(CreditsMonthsUser creditsMonthsUser : creditsMonth){ + String nickname = WechatUtil.decodeNickName(creditsMonthsUser.getNickname()); + creditsMonthsUser.setNickname(nickname); + } + return creditsMonth; +======= + Integer a = 100; + return creditsMonthsMapper.creditsMonthsQuery(months, year); +>>>>>>> change credits + } + +``` + +##### 日常开发流程应该是怎么样的呢? +1. git fetch origin dev 拉取远程的dev分支到本地,本地也会创建出dev分支 + +2.git checkout -b feature-dev 我们自己从dev分支创建出一个feature-dev分支,用于我们自己开发 + +3.我们自己在feature-dev开发了一些功能后,进行提交时,可能其他人往dev分支上提交了更新,我们需要将更新拿到本地 + +4. + +git checkout dev 切到dev分支 +git pull origin dev 将其他人在远程dev上的提交拉到本地 + +5. + +git checkout feature-dev + +git rebase dev 将dev上的更改应用到我们的feature-dev分支 + +然后可能会出现冲突,我们对冲突进行合并, + +然后对修改后的文件使用git add +文件名 进行添加, + +添加完成后使用git rebase --continue就可以继续,然后合并完成(如果需要中断rebase操作可以使用git rebase --abort) + +6.git checkout feature-dev + +git rebase feature-dev 就可以把feature-dev上合并好的更改拿到dev分支上 + +7. git push origin dev 就可以将dev分支上的更改提交到远程 \ No newline at end of file diff --git a/docs/HTTP.md b/docs/HTTP.md new file mode 100644 index 0000000..fbd2a92 --- /dev/null +++ b/docs/HTTP.md @@ -0,0 +1,359 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +#### [1.HTTPS建立连接的过程是怎么样的?](#HTTPS建立连接的过程是怎么样的?) +#### [2.HTTP的缓存策略是怎么样的?](#HTTP的缓存策略是怎么样的?) +#### [3. TCP三次握手和四次挥手是怎么样的?](#TCP三次握手和四次挥手是怎么样的?) +#### [4.TCP怎么保证可靠性的?](#TCP怎么保证可靠性的?) +#### [5.TCP拥塞控制怎么实现?](#TCP拥塞控制怎么实现?) +#### [6.close_wait太多怎么处理?](#close_wait太多怎么处理?) + +### HTTPS建立连接的过程是怎么样的? + +![img](../static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p1b3lpZ2VoYWl6ZWk=,size_16,color_FFFFFF,t_70.png) + +在发起连接之前,服务器会向证书机构申请SSL证书,流程是服务器将自己的公钥发给CA证书机构,CA证书机构会用自己的私钥对服务器的公钥加密,生成SSL证书给服务器,服务器将SSL证书存储后供之后建立安全连接使用。 + +1.客户端发起请求,TCP三次握手,跟服务器建立连接。 +如上图所示,在第 ② 步时服务器发送了一个SSL证书给客户端,SSL 证书中包含的具体内容有: + +(1)证书的发布机构CA + +(2)证书的有效期 + +(3)服务器的公钥 + +(4)证书所有者 + +(5)签名 + +3、客户端在接受到服务端发来的SSL证书时,会对证书的真伪进行校验,以浏览器为例说明如下: + +(1)首先浏览器读取证书中的证书所有者、有效期等信息进行一一校验 + +(2)浏览器开始查找操作系统中已内置的受信任的证书发布机构CA证书,与服务器发来的证书中的颁发者CA比对,用于校验证书是否为合法机构颁发 。 + +(3)如果找不到,浏览器就会报错,说明服务器发来的证书是不可信任的。 + +(4)如果找到,那么浏览器就会从操作系统中取出 颁发者CA 的公钥,然后对服务器发来的证书里面的签名进行解密 + +(5)浏览器使用相同的hash算法计算出服务器发来的证书的hash值,将这个计算的hash值与证书中签名做对比 + +(6)对比结果一致,则证明服务器发来的证书合法,没有被冒充 + +(7)此时浏览器就可以读取证书中的公钥,用于后续加密了。 + +假设没有CA,那么如果服务器返回的包含公钥的包被hack截取,然后hack也生成一对公私钥,他将自己的公钥发给客户端。hack得到客户端数据后,解密,然后再通过服务器的公钥加密发给服务器,这样数据就被hack获取。 +有了CA后,客户端根据内置的CA根证书,很容易识别出hack的公钥不合法,或者说hack的证书不合法。 + +### HTTP的缓存策略是怎么样的? + +HTTP 缓存主要分为强缓存和对比缓存两种,从优先级上看,强缓存大于对比缓存。 + +#### 强缓存 + +![img](../static/13002258-3191bc567f09cd99.png) + + 强缓缓存就是浏览器缓存数据库里有缓存数据就不再去向服务器发请求了 + +可以造成强制缓存的字段有Expires和Cache-Control两个: + +Expires:该字段标识缓存到期时间,是一个绝对时间,也就是服务器时间+缓存有效时间。 +缺点:如果客户端修改了本地时间,会造成缓存失效。如果本地时间与服务器时间不一致,也会导致缓存失效。 + +Cache-Control:该字段表示缓存最大有效时间,该时间是一个相对时间。 +使用相对时间的话,即使本地时间与服务器时间不一致,也不会导致缓存失效。 + 下面列举一下Cache-Control的字段可以带的值: + +> - max-age:即最大有效时间 +> - no-cache:表示没有缓存,即告诉浏览器该资源并没有设置缓存 +> - s-maxage:同max-age,但是仅用于共享缓存,如CDN缓存 +> - public:多用户共享缓存,默认设置 +> - private:不能够多用户共享,HTTP认证之后,字段会自动转换成private。 + +#### 对比缓存 + +![img](../static/13002258-488a103decf49453.png) + +对比缓存的实现原理时,先给给服务器发请求,并且带上缓存的资源文件的缓存标识,让服务端进行对比,如果资源文件没有更改,就只返回header部分,body为空,状态码是304,浏览器使用缓存的资源文件。如果数据有更改,就返回更新后的资源文件。 + +可以实现对比缓存的机制有Last-Modified/If-Modified-Since和Etag/If-None-Match两种: + +##### Last-Modified/If-Modified-Since +就是 请求的response header中会返回Last-Modified字段,代表资源文件最近的修改时间,发请求时 request header中会带上If-Modified-Since字段,代表上次获取的资源文件的最近修改时间,服务器判断资源文件是否有更新,来决定返回最新的资源文件(返回200),还是让浏览器使用缓存(返回304)。 + +缺点: + +1. Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间。 + +2. 如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存。 + +##### Etag/If-None-Match + +就是 response header中会返回Etag,代表资源文件的版本号,发请求时 request header中会加上If-None-Match字段,值是上次请求的资源的文件的版本号,代表上次请求的资源文件的版本号,服务器判断资源文件是否有更新,来决定返回最新的资源文件(返回200),还是让浏览器使用缓存(返回304)。 + +##### 优先级 + +优先级方面排序是 强缓存(Cache-Control)> 强缓存(Expires)> 对比缓存(Etag/If-None-Match)> 对比缓存(Last-Modified/If-Modified-Since) + +### TCP三次握手和四次挥手是怎么样的? + +#### 三次握手: + +TCP是一个面向连接的可靠的传输协议。建立连接前需要进行三次握手。 + +流程如下: + +![太厉害了,终于有人能把TCP/IP 协议讲的明明白白了](../static/f8ac11f0ec8942c597943f7ca1cbc150.jpeg) + +1.客户端首先会生成一个随机数J作为数据包的序号,给服务端发送一个SYN包,包的序号seq设置为J,发送成功后,自己进入SYN_SENT 状态,代表发送SYN包成功,等待服务端的确认。 + +2.服务端收到SYN包之后,会进入到SYN_RECV状态,同时为了检测服务端到客户端是否通畅,会给客户端发送一个SYN包,将ACK设置为1,并且会带上ack=J+1,生成随机数作为包的序号,seq=K,用于确认之前收到了客户端发送的SYN包。 + +3.客户端收到服务端发送的SYN包后,会检测ACK标志位是否为1,并且ack是否等于J+1,如果是的话,就说明之前服务器收到了客户端发送的SYN包,并且服务端发送给客户端的SYN包也是可以收到的,所以需要给服务端发送ACK包,ACK=1,ack=K+1。 + +4.服务端收到ACK回应后,检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。 + +#### 四次挥手 + +![img](../static/640-5050715.png) + +TCP是全双工的连接,断开每一方向的连接都需要发送断开请求,断开确认。所以总共需要发送4个包才能确定连接的断开。 + +1.首先客户端给服务端发送一个FIN包,seq=J,客户端进入FIN_WAIT_1状态,代表客户端已经没有数据发送给服务端了。 + +2.服务端接收到FIN包之后,会给客户端发送一个ACK,ack=J+1,用于确认客户端-服务端这边的连接进行断开。同时会进入到CLOSE_WAIT状态,表示在等待关闭,因为服务端往客户端可能还在继续传输数据,暂时还不能断开。客户收到服务端返回的ACK回应后,会进入到FIN_WAIT_2状态,代表客户端-服务端的连接已经断开成功,等待服务端发送FIN包断开服务端-客户端的连接。 + +3.服务端发现没有数据发给客户端后,会发一个FIN包给客户端,并且进入LAST_ACK,代表等待客户端的ACK,一旦收到ACK,代表连接正式断开,服务端可以进入CLOSE状态。 + +4.客户端收到服务端发送的FIN包后,说明连接已经断开了,但是需要服务端知道,客户端会给服务端发送一个ACK包通知服务端,并且会进入TIME_WATING状态,代表超过超时时间后自动进入CLOSE状态,如果ACK包中途丢了,服务端会再发送FIN包,客户端会进行ACK包重发,这也是TIME_WAITING状态的意义。 + +### TCP怎么保证可靠性的? + +TCP主要提供了**检验和**、**序列号/确认应答**、**超时重传**、**最大消息长度**、**滑动窗口控制**等方法实现了可靠性传输。 + +**校验和** + +主要是将数据切分成若干个16位的二进制串,将每个二进制串看成一个二进制数,对这些数进行相加等运算得到一个校验和,然后接收方收到数据会使用同样的算法来对数据计算校验和,如果结果和发送端发过来的校验和一样,那么就校验成功。主要是为了防止接收方收到的是已经损坏的数据。 + +**序列号/确认应答,超时重传** + +对发送的每个数据包都有一个序号,服务端收到数据包会返回一个ACK进行确认,如果在超过超时时间后,客户端还是没有收到服务端返回的ACK确认,就会对数据包进行超时重传。并且超时时间是动态变化的,初始值会大于正常的报文发送到接收到ACK回应的时间,重传后还没有得到回应会调大重传时间,然后进行重传。 +例如:在Linux中,超时以500ms为单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。其规律为:如果重发一次仍得不到应答,就等待2500ms后再进行重传,如果仍然得不到应答就等待4500ms后重传,依次类推,以指数形式递增,重传次数累计到一定次数后,TCP认为网络或对端主机出现异常,就会强行关闭连接。 + +**最大消息长度** + +在建立TCP连接的时候,双方约定一个最大的长度(MSS)作为发送的单位,重传的时候也是以这个单位来进行重传。理想的情况下是该长度的数据刚好不被网络层分块。 + +**滑动窗口控制** + +就是发送方在发送一个包时,不需要等到收到接收方对上一个包的确认应答后,才能发。只需要当前发送的包在滑动窗口内就行了。发送方每接收到一个确认应答,就会向后移动一位。 对于接收方而言,只会给客户端返回当前需要的包的序号,也就是对目前已收到的包的确认应答。(在拥塞控制里面,一般判断是否是网络拥塞导致丢包,有两种机制,超时重传,超过重传时间,还没有收到回应,另一个是收到三个重复确认ACK,就是比如中间某个数据包N丢了,那么后面发送的3个数据包如果被服务端接收到了,返回的确认回答ACK=N,代表没有收到数据包N,需要发送方重传,重复确认ACK超过3个,就认为发送了网络拥塞) + +![img](../static/640-4460300.jpeg) + +### 流量控制是怎么实现的? + +因为滑动窗口设置得太大或太小都不易于数据传输,所以是根据接收端的反馈,发送端可以对滑动窗口大小进行动态调整的。发送端在发送的数据包的序号必须小于最大的滑动窗口值,所以当发送的数据包过多,导致接收端的缓冲区写满时,接收端会通知给客户端将滑动窗口设置为更小的值,减少发送的量,达到一个流量控制的效果。 + +### TCP拥塞控制怎么实现? + +![img](../static/640-20200711182519652.jpeg) + +拥塞控制主要由**慢启动**,**拥塞避免**,**拥塞发生时算法**,**快速恢复**四个算法组成。 + +**慢启动** + +TCP连接刚建立,一点一点地提速,试探一下网络的承受能力,以免直接扰乱了网络通道的秩序,是呈指数增长的。一开始拥塞窗口cwnd大小是1,就是每收到一个ACK确认回答,就会把拥塞窗口cwnd的大小+1,这样在每个往返时延下,窗口cwnd大小都会变为原来的二倍,所以就会呈指数增长。 + +**拥塞避免** + +因为慢启动的拥塞窗口cwnd呈指数增长的,一旦达到网络的最大承受能力时,有可能已经发出大量数据包了,造成网络拥塞了,所以为了避免网络拥塞,当拥塞窗口cwnd达到慢启动的阀值ssthresh的大小时,就会停止指数增长,进入线性增长状态,在每个往返时延下,窗口cwnd大小都+1。 + +**拥塞发生时算法** + +一般认为,网络拥塞时就会发生网络丢包,所以判定拥塞发生就是以丢包为主。有两种判定方法,**超时重传**,在发送一个数据以后就开启一个计时器,在一定时间(RTO[Retransmission Timeout])内如果没有得到发送数据报的ACK报文,那么就重新发送数据,直到发送成功为止。另一个是**收到三个重复确认ACK**,就是比如中间某个数据包N丢了,那么后面发送的3个数据包如果被服务端接收到了,返回的确认回答ACK=N,代表没有收到数据包N,需要发送方重传,重复确认ACK超过3个,就认为发送了网络拥塞)。 + +一旦认定网络拥塞,就会将慢启动阀值ssthresh设置为发生拥塞时的窗口cwnd大小的一半。 + +PS:快重传是什么? + +假设发送方发送了1,2,3,4个数据包,假设接收方收到1,2数据包,并且发送了ACK确认,没有收到3,但是收到了4。根据可靠性传输原理,由于没有收到3,即便接受到了4,它也是一个失序报文,接收方不能对它进行确认。只能等发送方在等待3的ACK的回应时间超过2MSL后,进行重发。但是在这里为了让发送方快速知道哪些数据报文丢失了,接收方在收到3时就会给他返回2的ACK,一旦收到3个重复的ACK回应,也就是2的ACK,发送方就会意识到数据包3丢了,就会进行快速重传,重发报文3。 + +**快速恢复算法** + +发生网络拥塞后,慢启动阀值ssthresh设置为发生拥塞时的窗口cwnd大小的一半后, + +如果是早期的算法TCP Tahoe,此时会将cwnd重置为1,重新开始慢启动的过程。 + +如果现在的TCP Reno算发,会将cwnd窗口设置为新的ssthresh值的大小,后续开始进入拥塞避免算法的流程,对cwnd窗口进入线性增长的状态。 + +### close_wait太多怎么处理? + +close_wait 主要在TCP四次挥手时,服务端给客户端返回ACK应答后,由于自身还需要给客户端传输数据,所以会进入到close_wait状态,直到不需要给客户端发数据了,才会去给客户端发送FIN包,同时进入LAST_ACK状态。(被动关闭的一方没有及时发出 FIN 包就会导致自己一直处于close_wait状态。) + +tcp_keepalive_time默认是2个小时,也就是TCP空闲连接可以存活2个小时,在close_wait状态下,可以把这个时间调小,减少处于close_wait连接的数量 + +### time_wait太多是怎么造成的? +首先time_wait状态存在的意义是可以有效地终止TCP连接,因为主动关闭方发生ACK给被动关闭方后,需要等待2MSL的时间(MSL指的是报文最大有效存活时间,在linux下是60s),在这个时间内,如果没有收到被动关闭方重发的FIN包,就说明连接关闭完成了。 +在高并发短连接的业务场景下,由于短连接的传输数据+业务处理的时间很短,所以服务器处理完请求就会立即主动关闭连接,并且进入TIME_WAITING状态,而端口处于有个0~65535的范围中,除去系统占用的,总的数量有限。所以持续的到达一定量的高并发短连接,会使服务器因端口资源不足而拒绝为一部分请求服务。 +可以通过修改TCP的默认配置来改善这个问题。 +``` +net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭; +net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭; +net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。 +net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间 +``` +https://www.cnblogs.com/dadonggg/p/8778318.html +https://segmentfault.com/a/1190000019292140 +### HTTP/2 有哪些新特性? +#### 1.二进制传输 +HTTP/2传输数据量的大幅减少,主要有两个原因:以二进制方式传输和Header 压缩。我们先来介绍二进制传输,HTTP/2 采用二进制格式传输数据,而非HTTP/1.x 里纯文本形式的报文 ,二进制协议解析起来更高效。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。 +HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。 + +#### 2.Header 压缩 +HTTP/2并没有使用传统的压缩算法,而是开发了专门的"HPACK”算法,在客户端和服务器两端建立“字典”,用索引号表示重复的字符串,还采用哈夫曼编码来压缩整数和字符串,可以达到50%~90%的高压缩率。 + +具体来说: + +在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送; + +首部表在HTTP/2的连接存续期内始终存在,由客户端和服务器共同渐进地更新; + +每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值 + +例如下图中的两个请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销。 + +#### 3.多路复用 +在 HTTP/2 中引入了多路复用的技术。多路复用很好的解决了浏览器限制同一个域名下的请求数量的问题,同时也接更容易实现全速传输,毕竟新开一个 TCP 连接都需要慢慢提升传输速度。 + +https://my.oschina.net/u/4331678/blog/3628959 + +#### 4.Server Push + +HTTP2还在一定程度上改变了传统的“请求-应答”工作模式,服务器不再是完全被动地响应请求,也可以新建“流”主动向客户端发送消息。比如,在浏览器刚请求HTML的时候就提前把可能会用到的JS、CSS文件发给客户端,减少等待的延迟,这被称为"服务器推送"( Server Push,也叫 Cache push) +#### 5.提高安全性 + +出于兼容的考虑,HTTP/2延续了HTTP/1的“明文”特点,可以像以前一样使用明文传输数据,不强制使用加密通信,不过格式还是二进制,只是不需要解密。 + +但由于HTTPS已经是大势所趋,而且主流的浏览器Chrome、Firefox等都公开宣布只支持加密的HTTP/2,所以“事实上”的HTTP/2是加密的。也就是说,互联网上通常所能见到的HTTP/2都是使用"https”协议名,跑在TLS上面。HTTP/2协议定义了两个字符串标识符:“h2"表示加密的HTTP/2,“h2c”表示明文的HTTP/2。 + +### HTTP报文结构是怎么样的? + +#### 请求报文 + +请求报文结构如下: + +![img](../static/640-4553045.png) + + + +HTTP请求由请求行+请求header+空行+请求内容组成,第一行就是请求行,主要包含请求方法,URL,HTTP协议版本。第二行开始就是请求Header,请求Header后面会有一个空行,用于区分Header和请求内容。 + +#### 请求行 + +由请求方法字段、URL字段、协议版本字段三部分构成,它们之间由空格隔开。常用的请求方法有:GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。 + +#### 请求头 + +请求头由key/value对组成,每行为一对,key和value之间通过冒号(:)分割。请求头的作用主要用于通知服务端有关于客户端的请求信息。 + +典型的请求头有: + +User-Agent:生成请求的浏览器类型 + +Accept:客户端可识别的响应内容类型列表;星号`*` 用于按范围将类型分组。`*/*`表示可接受全部类型,`type/*`表示可接受type类型的所有子类型。 + +Accept-Language: 客户端可接受的自然语言 + +Accept-Encoding: 客户端可接受的编码压缩格式 + +Accept-Charset: 可接受的字符集 + +Host: 请求的主机名,允许多个域名绑定同一IP地址 + +connection:连接方式(close或keeplive) + +Cookie: 存储在客户端的扩展字段 + +#### 空行 + +最后一个请求头之后就是空行,用于告诉服务端以下内容不再是请求头的内容了。 + +#### 请求内容 + +请求内容主要用于POST请求,与POST请求方法配套的请求头一般有Content-Type(标识请求内容的类型)和Content-Length(标识请求内容的长度) + + + +#### 响应报文 + +![img](../static/640-20200712194015324.png) + +HTTP响应报文由状态行、响应头、空行和响应内容4个部分构成。第一行是状态行,由HTTP协议版本,状态码,状态描述组成,第二行开始是响应头,响应头后面是一个空行,用于区分响应头和响应内容。 + +#### 状态行 + +由HTTP协议版本、状态码、状态码描述三部分构成,它们之间由空格隔开。 + +状态码由3位数字组成,第一位标识响应的类型,常用的5大类状态码如下: + +1xx:表示服务器已接收了客户端的请求,客户端可以继续发送请求 + +2xx:表示服务器已成功接收到请求并进行处理 + +3xx:表示服务器要求客户端重定向 + +4xx:表示客户端的请求有非法内容 + +5xx:标识服务器未能正常处理客户端的请求而出现意外错误 + +常见状态码说明: + +200 OK: 表示客户端请求成功 + +304 Not Modified:未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源。 + +400 Bad Request: 表示客户端请求有语法错误,不能被服务器端解析 + +401 Unauthorized: 表示请求未经授权,该状态码必须与WWW-Authenticate报文头一起使用 + +404 Not Found:请求的资源不存在,例如输入了错误的url + +500 Internal Server Error: 表示服务器发生了不可预期的错误,导致无法完成客户端的请求 + +503 Service Unavailable:表示服务器当前不能处理客户端的请求,在一段时间后服务器可能恢复正常 + +#### 响应头 + +一般情况下,响应头会包含以下,甚至更多的信息。 + +Location:服务器返回给客户端,用于重定向到新的位置 + +Server: 包含服务器用来处理请求的软件信息及版本信息 + +Vary:标识不可缓存的请求头列表 + +Connection: 连接方式。 + +对于请求端来讲:close是告诉服务端,断开连接,不用等待后续的求请了。keeplive则是告诉服务端,在完成本次请求的响应后,保持连接,等待本次连接后的后续请求。 + +对于响应端来讲:close表示连接已经关闭。keeplive则表示连接保持中,可以继续处理后续请求。Keep-Alive表示如果请求端保持连接,则该请求头部信息表明期望服务端保持连接多长时间(秒),例如300秒,应该这样写Keep-Alive: 300 + +#### 空行 + +最后一个响应头之后就是空行,用于告诉请求端以下内容不再是响应头的内容了。 + +#### 响应内容 + +服务端返回给请求端的文本信息。 + +下面是一个实际的例子: + +![img](../static/640-4554190.jpeg) + +##### HTTP状态码502,503,504各自代表着什么? + +502是指网关(一般是Nginx)从后端服务器接受到了无效的响应结果。通常是我们的后端服务器挂了之类的。 + +503是请求过载,就是请求超过了Nginx限流设置的阀值,就会返回503服务不可用。 + +504是指网关(一般是Nginx)从后端服务器接受的响应超时了。 \ No newline at end of file diff --git a/docs/HashMap.md b/docs/HashMap.md new file mode 100644 index 0000000..95ae725 --- /dev/null +++ b/docs/HashMap.md @@ -0,0 +1,1009 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +下面是主要是自己看了很多Java容器类相关的博客,以及很多面经中涉及到的Java容器相关的面试题后,自己全部手写的解答,也花了一些流程图,之后会继续更新这一部分。 + +#### [1.HashMap添加一个键值对的过程是怎么样的?](#HashMap添加一个键值对的过程是怎么样的?) + +#### [2.ConcurrentHashMap添加一个键值对的过程是怎么样的?](#ConcurrentHashMap添加一个键值对的过程是怎么样的?) +#### [3.HashMap与HashTable,ConcurrentHashMap的区别是什么?](#HashMap与HashTable,ConcurrentHashMap的区别是什么?) +#### [4.HashMap扩容后是否需要rehash?](#HashMap扩容后是否需要rehash?) + +#### [5.HashMap扩容是怎样扩容的,为什么都是2的N次幂的大小?](#HashMap扩容是怎样扩容的,为什么都是2的N次幂的大小?) +#### [6.ConcurrentHashMap是怎么记录元素个数size的?](#ConcurrentHashMap是怎么记录元素个数size的?) +#### [7.为什么ConcurrentHashMap,HashTable不支持key,value为null?](#为什么ConcurrentHashMap,HashTable不支持key,value为null?) +#### [8.HashSet和HashMap的区别?](#HashSet和HashMap的区别? ) +#### [9.HashMap遍历时删除元素的有哪些实现方法?](#HashMap遍历时删除元素的有哪些实现方法?) + + +### HashMap添加一个键值对的过程是怎么样的? + +流程图如下: + +![image.png](../static/2.png) + +#### 1.初始化table + +判断table是否为空或为null,否则执行resize()方法(resize方法一般是扩容时调用,也可以调用来初始化table)。 + +#### 2.计算hash值 + +根据键值key计算hash值。(因为hashCode是一个int类型的变量,是4字节,32位,所以这里会将hashCode的低16位与高16位进行一个异或运算,来保留高位的特征,以便于得到的hash值更加均匀分布) + +```java +static final int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); +} +``` + +#### 3.插入或更新节点 + +根据(n - 1) & hash计算得到插入的数组下标i,然后进行判断 + +##### 3.1.数组为空(table[i]==null) + + 那么说明当前数组下标下,没有hash冲突的元素,直接新建节点添加。 + +##### 3.2.等于下标首元素,table[i].hash == hash &&(table[i]== key || (key != null && key.equals(table[i].key))) + + 判断table[i]的首个元素是否和key一样,如果相同直接更新value。 + +##### 3.3.数组下标存的是红黑树,table[i] instanceof TreeNode + +判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对。 + +##### 3.4.数组下标存的是链表 + + 上面的判断条件都不满足,说明table[i]存储的是一个链表,那么遍历链表,判断是否存在已有元素的key与插入键值对的key相等,如果是,那么更新value,如果没有,那么在链表末尾插入一个新节点。插入之后判断链表长度是否大于8,大于8的话把链表转换为红黑树。 + +#### 4.扩容 + +插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold(一般是数组长度*负载因子0.75),如果超过,进行扩容。 + +源代码如下: + +```java +final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + Node[] tab; Node p; int n, i; + // 1.tab为空则创建 + if ((tab = table) == null || (n = tab.length) == 0) + n = (tab = resize()).length; + // 2.计算index,并对null做处理 + // 3.插入元素 + //(n - 1) & hash 确定元素存放在哪个数组下标下 + //下标没有元素,新生成结点放入中(此时,这个结点是放在数组中) + if ((p = tab[i = (n - 1) & hash]) == null) + tab[i] = newNode(hash, key, value, null); + // 下标中已经存在元素 + else { + Node e; K k; + // 节点key存在,直接覆盖value + // 比较桶中第一个元素(数组中的结点)的hash值相等,key相等 + if (p.hash == hash && + ((k = p.key) == key || (key != null && key.equals(k)))) + // 将第一个元素赋值给e,用e来记录 + e = p; + // 判断该链为红黑树 + // hash值不相等,即key不相等;为红黑树结点 + else if (p instanceof TreeNode) + // 放入树中 + e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); + // 该链为链表 + // 为链表结点 + else { + // 在链表最末插入结点 + for (int binCount = 0; ; ++binCount) { + // 到达链表的尾部 + if ((e = p.next) == null) { + // 在尾部插入新结点 + p.next = newNode(hash, key, value, null); + // 结点数量达到阈值,转化为红黑树 + if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st + treeifyBin(tab, hash); + // 跳出循环 + break; + } + // 判断链表中结点的key值与插入的元素的key值是否相等 + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + // 相等,跳出循环 + break; + // 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表 + p = e; + } + } + // 表示在桶中找到key值、hash值与插入元素相等的结点 + if (e != null) { + // 记录e的value + V oldValue = e.value; + // onlyIfAbsent为false或者旧值为null + if (!onlyIfAbsent || oldValue == null) + //用新值替换旧值 + e.value = value; + // 访问后回调 + afterNodeAccess(e); + // 返回旧值 + return oldValue; + } + } + ++modCount; + // 超过最大容量 就扩容 + // 实际大小大于阈值则扩容 + if (++size > threshold) + resize(); + // 插入后回调 + afterNodeInsertion(evict); + return null; +} +``` + +### ConcurrentHashMap添加一个键值对的过程是怎么样的? + +这是我自己阅读源码后,比HashMap的会复杂很多,画的一个流程图如下所示: + +![image.png](../static/3.png) + +#### 1.判断null值 + +判断key==null 或者 value == null,如果是,抛出空指针异常。 +#### 2.计算hash + +根据key计算hash值(计算结果跟HashMap是一致的,写法不同)。 +#### 3.进入for循环,插入或更新元素 + +* ##### 3.1 如果 tab==null || tab.length==0,说明数组未初始化 + + 说明当前数组tab还没有初始化。 + + 那么调用initTable()方法初始化tab。(在initTable方法中,为了控制只有一个线程对table进行初始化,当前线程会通过**CAS操作对SIZECTL变量赋值为-1**,如果赋值成功,线程才能初始化table,否则会调用Thread.yield()方法让出时间片)。 + +* ##### 3.2 如果f ==null,说明当前下标没有哈希冲突的键值对 + + 说明当前数组下标还没有哈希冲突的键值对。 + + Node f是根据key的hash值计算得到数组下标,下标存储的元素,f可能是null,也有可能是链表头节点,红黑树根节点或迁移标志节点ForwardNode) + + 那么根据key和value创建一个Node,使用**CAS操作设置在当前数组下标下**,并且break出for循环。 + +* ##### 3.3 如果f != null && f.hash = -1,说明存储的是标志节点,表示在扩容 + + 说明ConcurrentHashMap正在在扩容,当前的节点f是一个标志节点,当前下标存储的hash冲突的元素已经迁移了。 + + 那么当前线程会调用helpTransfer()方法来辅助扩容,扩容完成后会将tab指向新的table,然后继续执行for循环。 + +* ##### 3.4 除上面三种以外情况,说明是下标存储链表或者是红黑树 + + 说明f节点是一个链表的头结点或者是红黑树的根节点,那么对f加sychronize同步锁,然后进行以下判断: + + * f.hash > 0 + + 如果是f的hash值大于0,当前数组下标存储的是一个链表,f是链表的头结点。 + + 对链表进行遍历,如果有节点跟当前需要插入节点的hash值相同,那么对节点的value进行更新,否则根据key,value创建一个Node,添加到链表末尾。 + + * f instanceof TreeBin + + 如果f是TreeBin类型,那么说明当前数组下标存储的是一个红黑树,f是红黑树的根节点,调用putTreeVal方法,插入或更新节点。 + + + 如果f是TreeBin类型,那么说明当前数组下标存储的是一个红黑树,f是红黑树的根节点,调用putTreeVal方法,插入或更新节点。 + + + +* 插入完成后,判断binCount(数组下标存储是一个链表时,binCount是链表长度),当binCount超过8时,**并且数组的长度大于64时**,那么调用treeifyBin方法将链表转换为红黑树。最后break出for循环。 + +### PS: + +很多技术文章都是说链表长度大于8就转换为红黑树,我当时也没有注意这个细节,直到有个群里的朋友指正,当原来的链表长度超过8时,确实会调用treeifyBin方法,但是在treeifyBin方法中会判断**当前tab是否为空,或者数组长度是否小于64**,如果满足条件,那么调用resize方法对tab初始化或者扩容,就不会将链表转换为红黑树了。 + +添加键值对的putVal方法的源码: + +![image](../static/12609483-f49199a436960c2b.png) + +
+ +treeifyBin方法的源码: MIN_TREEIFY_CAPACITY是64 + +![image](../static/12609483-31cdd1cc821d9c8d.png) + + + + +#### 4.判断是否需要扩容 + +调用addCount()对当前数组长度加1,在addCount()方法中,会判断当前元素个数是否超过sizeCtl(扩容阈值,总长度*0.75),如果是,那么会进行扩容,如果正处于扩容过程中,当前线程会辅助扩容。 + +**ConcurrentHashMap源码:** + +```java +final V putVal(K key, V value, boolean onlyIfAbsent) { + //ConcurrentHashMap不允许key和value为null,否则抛出异常 + if (key == null || value == null) throw new NullPointerException(); + int hash = spread(key.hashCode());//计算hash值 + int binCount = 0; + for (Node[] tab = table;;) {//进入循环 + Node f; int n, i, fh; + if (tab == null || (n = tab.length) == 0)//数组如果为空 + tab = initTable();//初始化数组 + else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { + if (casTabAt(tab, i, null, + new Node(hash, key, value, null)))//如果发现此数组下标下没有哈希冲突的元素,就直接使用CAS操作将Node设置到此下标下 + break; + } + else if ((fh = f.hash) == MOVED)//代表当前下标的头结点是标识节点,代表数组在扩容 + tab = helpTransfer(tab, f);//协助扩容 + else {//这种是普遍情况,存的是链表或者红黑树,进行插入 + V oldVal = null; + synchronized (f) {//加同步锁,避免多线程进行插入 + if (tabAt(tab, i) == f) { + if (fh >= 0) {//头结点hash值大于0,此数组下标下代表存的是一个链表 + binCount = 1; + for (Node e = f;; ++binCount) {//遍历链表 + K ek; + if (e.hash == hash && + ((ek = e.key) == key || + (ek != null && key.equals(ek)))) { + oldVal = e.val; + if (!onlyIfAbsent) + e.val = value; + break; + } + Node pred = e; + if ((e = e.next) == null) {//键值对是新添加的,在链表尾部插入新节点 + pred.next = new Node(hash, key, + value, null); + break; + } + } + } + else if (f instanceof TreeBin) {//下标下存的是红黑树 + Node p; + binCount = 2; + if ((p = ((TreeBin)f).putTreeVal(hash, key, + value)) != null) { + oldVal = p.val; + if (!onlyIfAbsent) + p.val = value; + } + } + } + } + if (binCount != 0) { + if (binCount >= TREEIFY_THRESHOLD)//链表长度>=8,转换为红黑树 + treeifyBin(tab, i); + if (oldVal != null) + return oldVal; + break; + } + } + } + addCount(1L, binCount); + return null; +} +``` + +### HashMap与HashTable,ConcurrentHashMap的区别是什么? + +主要从底层数据结构,线程安全,执行效率,是否允许Null值,初始容量及扩容,hash值计算来进行分析。 + +#### 1.底层数据结构 + +```java +transient Node[] table; //HashMap + +transient volatile Node[] table;//ConcurrentHashMap + +private transient Entry<?,?>[] table;//HashTable +``` + +#### HashMap=数组+链表+红黑树 +HashMap的底层数据结构是一个数组+链表+红黑树,数组的每个元素存储是一个链表的头结点,链表中存储了一组哈希值冲突的键值对,通过链地址法来解决哈希冲突的。为了避免链表长度过长,影响查找元素的效率,当链表的长度>8时,会将链表转换为红黑树,链表的长度<6时,将红黑树转换为链表(但是红黑树转换为链表的时机不是在删除链表时,而是在扩容时,发现红黑树分解后的两个链表<6,就按链表处理,否则就建立两个小的红黑树,设置到扩容后的位置)。之所以临界点为8是因为红黑树的查找时间复杂度为logN,链表的平均时间查找复杂度为N/2,当N为8时,logN为3,是小于N/2的,正好可以通过转换为红黑树减少查找的时间复杂度。 + +#### ConcurrentHashMap=数组+链表+红黑树 + +ConcurrentHashMap底层数据结构跟HashMap一致,底层数据结构是一个数组+链表+红黑树。只不过使用了volatile来进行修饰它的属性,来保证内存可见性(一个线程修改了这些属性后,会使得其他线程中对于该属性的缓存失效,以便下次读取时取最新的值)。 + +#### Hashtable=数组+链表 +Hashtable底层数据结构跟HashMap一致,底层数据结构是一个数组+链表,也是通过链地址法来解决冲突,只是链表过长时,不会转换为红黑树来减少查找时的时间复杂度。Hashtable属于历史遗留类,实际开发中很少使用。 + +#### 2.线程安全 + +##### HashMap 非线程安全 + +HashMap是非线程安全的。(例如多个线程插入多个键值对,如果两个键值对的key哈希冲突,可能会使得两个线程在操作同一个链表中的节点,导致一个键值对的value被覆盖) + +##### HashTable 线程安全 +HashTable是线程安全的,主要通过使用synchronized关键字修饰大部分方法,使得每次只能一个线程对HashTable进行同步修改,性能开销较大。 +##### ConcurrentHashMap 线程安全 +ConcurrentHashMap是线程安全的,主要是通过CAS操作+synchronized来保证线程安全的。 + +##### CAS操作 + +往ConcurrentHashMap中插入新的键值对时,如果对应的数组下标元素为null,那么通过CAS操作原子性地将节点设置到数组中。 + +```java +//这是添加新的键值对的方法 +final V putVal(K key, V value, boolean onlyIfAbsent) { +...其他代码 + if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { + if (casTabAt(tab, i, null, new Node(hash, key, value, null))) break; // 因为对应的数组下标元素为null,所以null作为预期值,new Node(hash, key, value, null)作为即将更新的值,只有当内存中的值与即将预期值一致时,才会进行更新,保证原子性。 + } +...其他代码 +} +static final boolean casTabAt(Node[] tab, int i, + Node c, Node v) { + return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v); +} +``` + +##### synchronized锁 + +往ConcurrentHashMap中插入新的键值对时,如果对应的数组下标元素不为null,那么会对数组下标存储的元素(也就是链表的头节点)加synchronized锁, 然后进行插入操作, + +```java +Node f = tabAt(tab, i = (n - 1) & hash)); +synchronized (f) {//f就是数组下标存储的元素 + if (tabAt(tab, i) == f) { + if (fh >= 0) {//当前下标存的是链表 + binCount = 1; + for (Node e = f;; ++binCount) {//遍历链表 + K ek; + if (e.hash == hash && + ((ek = e.key) == key || + (ek != null && key.equals(ek)))) { + oldVal = e.val; + if (!onlyIfAbsent) + e.val = value; + break; + } + Node pred = e; + if ((e = e.next) == null) { + pred.next = new Node(hash, key, + value, null); + break; + } + } + } + else if (f instanceof TreeBin) {//当前下标存的是红黑树 + Node p; + binCount = 2; + if ((p = ((TreeBin)f).putTreeVal(hash, key, + value)) != null) { + oldVal = p.val; + if (!onlyIfAbsent) + p.val = value; + } + } + } +} + +``` + +#### 3.执行效率 + +因为HashMap是非线程安全的,执行效率会高一些,其次是ConcurrentHashMap,因为HashTable在进行修改和访问时是对整个HashTable加synchronized锁,多线程访问时,同一时间点,只有一个线程可以访问或者添加键值对,所以效率最低。 + +#### 4.是否允许null值出现 + +HashMap的key和value都可以为null,如果key为null,那么计算的hash值会是0,最终计算得到的数组下标也会是0,所以key为null的键值对会存储在数组中的首元素的链表中。value为null的键值对也能正常插入,跟普通键值对插入过程一致。 + +```java +static final int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); +} +``` + +HashTable的键和值都不能为null,如果将HashTable的一个键值对的key设置为null,因为null值没法调用hashCode()方法获取哈希值,所以会抛出空指针异常。同样value为null时,在put方法中会进行判断,然后抛出空指针异常。 + +```java +public synchronized V put(K key, V value) { + // Make sure the value is not null + if (value == null) { + throw new NullPointerException(); + } + Entry<?,?> tab[] = table; + int hash = key.hashCode(); + //...其他代码 +} +``` + +ConcurrentHashMap的键和值都不能为null,在putVal方法中会进行判断,为null会抛出空指针异常。 + +```java +final V putVal(K key, V value, boolean onlyIfAbsent) { + if (key == null || value == null) throw new NullPointerException(); + int hash = spread(key.hashCode()); + ...其他代码 +} +``` + +#### 5.初始容量及扩容 + +##### 不指定初始容量 +如果不指定初始容量,HashMap和ConcurrentHashMap默认会是16,HashTable的容量默认会是11。 +##### 指定初始容量 +如果指定了初始容量,HashMap和ConcurrentHashMap的容量会是比初始容量稍微大一些的2的幂次方大小,HashTable会使用初始容量, +##### 扩容 +扩容时,如果远长度是N,HashMap和ConcurrentHashMap扩容时会是2N,HashTable则是2N+1。 + +#### 6.hash值计算 + +HashTable会扩容为2N+1,HashTable之所以容量取11,及扩容时是是2N+1,是为了保证 HashTable的长度是一个素数,因为数组的下标是用key的hashCode与数组的长度取模进行计算得到的,而当数组的长度是素数时,可以保证计算得到的数组下标分布得更加均匀,可以看看这篇文章http://zhaox.github.io/algorithm/2015/06/29/hash + +```java +public synchronized V put(K key, V value) { + ...其他代码 + // Makes sure the key is not already in the hashtable. + Entry<?,?> tab[] = table; + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % tab.length; + ...其他代码 +} +``` + +HashMap和ConcurrentHashMap的hash值都是通过将key的hashCode()高16位与低16位进行异或运算(这样可以保留高位的特征,避免一些key的hashCode高位不同,低位相同,造成hash冲突),得到hash值,然后将hash&(n-1)计算得到数组下标。(n为数组的长度,因为当n为2的整数次幂时,hash mod n的结果在数学上等于hash&(n-1),而计算机进行&运算更快,所以这也是HashMap的长度总是设置为2的整数次幂的原因) + +```java +//HashMap计算hash值的方法 +static int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); +} +//ConcurrentHashMap计算hash值的方法 +static int spread(int h) {//h是对象的hashCode + return (h ^ (h >>> 16)) & HASH_BITS;// HASH_BITS = 0x7fffffff; +} +``` + +### HashMap扩容后是否需要rehash? + +在JDK1.8以后,不需要rehash,因为键值对的Hash值主要是根据key的hashCode()的高16位与低16位进行异或计算后得到,根据hash%length,计算得到数组的下标index,因为length是2的整数次幂,当扩容后length变为原来的两倍时,hash%(2*length)的计算结果结果差别在于第length位的值是1还是0,如果是0,那么在新数组中的index与旧数组的一直,如果是1,在新数组中的index会是旧数组中的数组中的index+length。 + + +### HashMap扩容是怎样扩容的,为什么都是2的N次幂的大小? + +#### 触发扩容 + +在没有指定初始长度的情况下,HashMap数组的默认长度为16,在添加一个新的键值对时,会调用putVal()方法,在方法中,成功添加一个新的键值对以后,会判断当前的元素个数是否超过阀值(数组长度*负载因子0.75),如果超过那么调用resize方法进行扩容。具体的扩容步骤如下: + +#### 计算扩容后的长度 + +* ##### 如果当前table为null + + 那么直接初始化一个数组长度为16的数组返回。 + +* ##### 如果当前table的length已经大于HashMap指定的最大值2的30次方 + + 那么直接返回旧table,不进行扩容。 + +* ##### 其他情况 + + 将table的length扩容为2倍,然后计算新的扩容阀值(新数组长度*0.75)。 +#### 初始化新数组 + +会根据扩容后的数组长度初始化话一个新的数组,并且直接赋值给当前hashMap的成员变量table。 + +```java +Node[] newTab = (Node[])new Node[newCap]; +table = newTab; +``` + +这一步就很有意思,也是HashMap是非线程安全的表现之一,因为此时newTab还是一个空数组,如果有其他线程访问HashMap,根据key去newTab中找键值对,会返回null。实际上可能key是有对应的键值对的,只不过键值对都保存在旧table中,还没有迁移过来。 + +(与之相反,HashTable在解决扩容时其他线程访问的问题,是通过对大部分方法使用sychronized关键字修饰,也就是某个线程在执行扩容方法时,会对HashTable对象加锁,其他线程无法访问HashTable。ConcurrentHashMap在解决扩容时其他线程访问的问题,是通过设置**ForwardingNode标识节点**来解决的,扩容时,某个线程对数组中某个下标下所有Hash冲突的元素进行迁移时,那么会将数组下标的数组元素设置为一个**标识节点ForwardingNode**,之后其他线程在访问时,如果发现key的hash值映射的数组下标对应是一个**标识节点ForwardingNode**(ForwardingNode继承于普通Node,区别字啊呀这个节点的hash值会设置为-1,并且会多一个指向扩容过程中新tab的指针nextTable),那么会根据ForwardingNode中的nextTable变量,去新的tab中查找元素。(如果是添加新的键值对时发现是ForwardingNode,当前线程会进行辅助扩容或阻塞等待,扩容完成后去新数组中更新或插入元素) + +#### 迁移元素 + +因为HashMap的数组长度总是2的N次幂,扩容后也是变为原来的2倍,所以有一个数学公式,当length为2的N次幂时, + +```java +hash%length=hash&(length-1) +``` + +而因为length是2的N次幂,length-1在二进制中其实是N个1。例如: + +length为16,length用2进制表示是10000, + +length-1是15,用2进制表示是1111, + +2*length为32,length用2进制表示是100000, + +2*length-1为31,length用2进制表示是11111, + +所以hash&(length-1)的计算结果与hash&(2*length-1)的计算结果差别在于扩容后需要多看一位,也就是看第N位的1与hash值的&结果。 + +假设原数组长度为16,length-1二进制表示为1111。key1的hash值为9,二进制表示为01001,key2的hash值为25,11001, + +所以hash&(length-1)的结果只要看低4位的结果,9和25的低4位都是1001,所以计算结果一致,计算结果都是9,所以在数组中处于数组下标为9的元素链表中。 + +扩容后数组长度为32,length-1二进制表示为11111,key1的hash值为9,二进制表示为01001,key2的hash值为25,11001, + +所以hash&(2*length-1)的结果需要看低5位的结果,9和25的低4位都是1001,所以计算结果不一致,计算结果都是9和25,因为key2的hash值的第五位为1,key1的hash值的第五位为0,所以会多16,也就是原数组长度的大小。 + +所以原数组同一下标index下的链表存储的hash冲突的元素,扩容后在新数组中的下标newIndex要么为index,要么为index+length(去决定于hash值的第N位为1,还是0,也就是hash&length的结果,原数组长度length为2的N-1次幂) + +所以会遍历链表(或者红黑树),然后对数组下标index下每个节点计算hash&length的结果,然后存放在两个不同的临时链表中,遍历完成后,hash&length结果为0的元素组成的临时链表会存储在新数组index位置,hash&length结果为1的元素组成的临时链表会存储在新数组index+length位置。 + +#### ConcurrentHashMap是怎么记录元素个数size的? + +HashMap默认是非线程安全的,可以认为每次只有一个线程来执行操作,所以hashMap就使用一个很简单的int类型的size变量来记录HashMap键值对数量就行了。 + +HashMap记录键值对数量的实现如下: + +```java +transient int size; +public int size() { + return size; +} +``` + +ConcurrentHashMap记录键值对数量的实现如下: + +```java +//size方法最大只能返回Integer.MAX_VALUE +public int size() { + long n = sumCount(); + return ((n < 0L) ? 0 : (n > (long)Integer.MAX_VALUE) ?Integer.MAX_VALUE : (int)n); +} + +//mappingCount方法可以返回long类型的最大值, +public long mappingCount() { + long n = sumCount(); + return (n < 0L) ? 0L : n; // ignore transient negative values +} + +private transient volatile long baseCount; +private transient volatile CounterCell[] counterCells; + +//sumCount会返回sumCount加上CounterCells数组中每个元素as存储的value +final long sumCount() { + CounterCell[] as = counterCells; CounterCell a; + long sum = baseCount; + if (as != null) { + for (int i = 0; i < as.length; ++i) { + if ((a = as[i]) != null) + sum += a.value; + } + } + return sum; +} + +@sun.misc.Contended //这个注解可以避免伪共享,提升性能。加与不加,性能差距达到了 5 倍。在缓存系统中,由于一个缓存行是出于32-256个字节之间,常见的缓存行为64个字节。而一般的变量可能达不到那么多字节,所以会出现多个相互独立的变量存储在一个缓存行中的情况,此时即便多线程访问缓存行上相互独立变量时,也涉及到并发竞争,会有性能开销,加了@sun.misc.Contended这个注解,在jDK8中,会对对象前后都增加128字节的padding,使用2倍于大多数硬件缓存行的大小来避免相邻扇区预取导致的伪共享冲突。 +static final class CounterCell { + volatile long value; + CounterCell(long x) { value = x; } +} +``` + +每次添加x个新的键值对后,会调用addCount()方法使用CAS操作对baseCount+x,如果操作失败,那么会新建一个CounterCell类型的对象,保存新增的数量x,并且将对象添加到CounterCells数组中去。 + +### 为什么ConcurrentHashMap,HashTable不支持key,value为null? + +因为HashMap是非线程安全的,默认单线程环境中使用,如果get(key)为null,可以通过containsKey(key) +方法来判断这个key的value为null,还是不存在这个key,而ConcurrentHashMap,HashTable是线程安全的, +在多线程操作时,因为get(key)和containsKey(key)两个操作和在一起不是一个原子性操作,可能在containsKey(key)时发现存在这个键值对,但是get(key)时,有其他线程删除了键值对,导致get(key)返回的Node是null,然后执行方法时抛出异常。所以无法区分value为null还是不存在key。 +至于ConcurrentHashMap,HashTable的key不能为null,主要是设计者的设计意图。 + +### HashSet和HashMap的区别? + +HashMap主要是用于存储非重复键值对,HashSet存储非重复的对象。虽然HashMap是继承于AbstractMap,实现了Map接口,HashSet继承于AbstractSet,实现了Set接口。但是由于它们都有去重的需求,所以HashSet主要实现都是基于HashMap的(如果需要复用一个类,我们可以使用继承模式,也可以使用组合模式。组合模式就是将一个类作为新类的组成部分,以此来达到复用的目的。)例如,在HashSet类中,有一个HashMap类型的成员变量map,这就是组合模式的应用。 + +```java + public class HashSet + extends AbstractSet + implements Set, Cloneable, java.io.Serializable +{ + private transient HashMap map; + private static final Object PRESENT = new Object();//占位对象 + public HashSet() { + map = new HashMap<>(); + } + public boolean add(E e) { + return map.put(e, PRESENT)==null;//占位对象 + } +} +``` + +在HashSet的构造方法中,创建了一个HashMap,赋值给map属性,之后在添加元素时,就是将元素作为key添加到HashMap中,只不过value是一个占位对象PRESENT。除了 `clone()`、`writeObject()`、`readObject()`是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。那么HashMap又是如何实现key不重复的呢? + +在调用HashMap的putVal方法添加新的键值对时,会进行如下操作: + +1.根据key计算hash值。 + +2.根据hash值映射数组下标,然后获取数组下标的对应的元素。 + +3.数组下标存储的是一个链表,链表包含了哈希冲突的元素,会对链表进行遍历,判断hash1==hash2,除此以外,还必须要key1==key2,或者key1.equals(key2)。 + +因为两个不同的对象的hashCode可能相等,但是相同的对象的hashCode肯定相等, + +==是判断两个变量或实例是不是指向同一个内存地址,如果是同一个内存地址,对象肯定相等。 + +``` +int hash = hash(key);//根据key计算hash值 +p = tab[i = (n - 1) & hash];//根据hash值映射数组下标,然后获取数组下标的对应的元素。 +for (int binCount = 0; ; ++binCount) {//数组下标存储的是一个链表,链表包含了哈希冲突的元素,会对链表进行遍历,判断每个节点的hash值与插入元素的hash值是否相等,并且是存储key对象的地址相等,或者key相等。 +if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; +} +``` + +### HashMap遍历时删除元素的有哪些实现方法? +首先结论如下: + +第1种方法 - for-each遍历HashMap.entrySet,使用HashMap.remove()删除(结果:抛出异常)。 + +第2种方法-for-each遍历HashMap.keySet,使用HashMap.remove()删除(结果:抛出异常)。 + +第3种方法-使用HashMap.entrySet().iterator()遍历删除(结果:正确删除)。 + +下面让我们来详细探究一下原因吧! + +HashMap的遍历删除方法与ArrayList的大同小异,只是api的调用方式不同。首先初始化一个HashMap,我们要删除key包含"3"字符串的键值对。 +```java +HashMap hashMap = new HashMap(); +hashMap.put("key1",1); +hashMap.put("key2",2); +hashMap.put("key3",3); +hashMap.put("key4",4); +hashMap.put("key5",5); +hashMap.put("key6",6); +``` +### 第1种方法 - for-each遍历HashMap.entrySet,使用HashMap.remove()删除(结果:抛出异常) +```java +for (Map.Entry entry: hashMap.entrySet()) { + String key = entry.getKey(); + if(key.contains("3")){ + hashMap.remove(entry.getKey()); + } + System.out.println("当前HashMap是"+hashMap+" 当前entry是"+entry); + +} +``` +输出结果: +```java +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 当前entry是key1=1 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 当前entry是key2=2 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 当前entry是key5=5 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 当前entry是key6=6 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4} 当前entry是key3=3 +Exception in thread "main" java.util.ConcurrentModificationException + at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429) + at java.util.HashMap$EntryIterator.next(HashMap.java:1463) + at java.util.HashMap$EntryIterator.next(HashMap.java:1461) + at com.test.HashMapTest.removeWayOne(HashMapTest.java:29) + at com.test.HashMapTest.main(HashMapTest.java:22) +``` + +### 第2种方法-for-each遍历HashMap.keySet,使用HashMap.remove()删除(结果:抛出异常) +```java +Set keySet = hashMap.keySet(); +for(String key : keySet){ + if(key.contains("3")){ + keySet.remove(key); + } + System.out.println("当前HashMap是"+hashMap+" 当前key是"+key); +} +``` +输出结果如下: +```java +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 当前key是key1 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 当前key是key2 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 当前key是key5 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 当前key是key6 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4} 当前key是key3 +Exception in thread "main" java.util.ConcurrentModificationException + at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429) + at java.util.HashMap$KeyIterator.next(HashMap.java:1453) + at com.test.HashMapTest.removeWayTwo(HashMapTest.java:40) + at com.test.HashMapTest.main(HashMapTest.java:23) +``` +### 第3种方法-使用HashMap.entrySet().iterator()遍历删除(结果:正确删除) +```java +Iterator> iterator = hashMap.entrySet().iterator(); +while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if(entry.getKey().contains("3")){ + iterator.remove(); + } + System.out.println("当前HashMap是"+hashMap+" 当前entry是"+entry); +} +``` +输出结果: +```java +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 当前entry是key1=1 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 当前entry是key2=2 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 当前entry是key5=5 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 当前entry是key6=6 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 当前entry是key4=4 +当前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4} 当前entry是deletekey=3 +``` +第1种方法和第2种方法抛出ConcurrentModificationException异常与上面ArrayList错误遍历-删除方法的原因一致,HashIterator也有一个expectedModCount,在遍历时获取下一个元素时,会调用next()方法,然后对 +expectedModCount和modCount进行判断,不一致就抛出ConcurrentModificationException异常。 +```java +abstract class HashIterator { + Node next; // next entry to return + Node current; // current entry + int expectedModCount; // for fast-fail + int index; // current slot + + HashIterator() { + expectedModCount = modCount; + Node[] t = table; + current = next = null; + index = 0; + if (t != null && size > 0) { // advance to first entry + do {} while (index < t.length && (next = t[index++]) == null); + } + } + + public final boolean hasNext() { + return next != null; + } + + final Node nextNode() { + Node[] t; + Node e = next; + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + if (e == null) + throw new NoSuchElementException(); + if ((next = (current = e).next) == null && (t = table) != null) { + do {} while (index < t.length && (next = t[index++]) == null); + } + return e; + } + + public final void remove() { + Node p = current; + if (p == null) + throw new IllegalStateException(); + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + current = null; + K key = p.key; + removeNode(hash(key), key, null, false, false); + expectedModCount = modCount; + } +} + +``` + +### PS:ConcurrentModificationException是什么? + +根据ConcurrentModificationException的文档介绍,一些对象不允许并发修改,当这些修改行为被检测到时,就会抛出这个异常。(例如一些集合不允许一个线程一边遍历时,另一个线程去修改这个集合)。 + +一些集合(例如Collection, Vector, ArrayList,LinkedList, HashSet, Hashtable, TreeMap, AbstractList, Serialized Form)的Iterator实现中,如果提供这种并发修改异常检测,那么这些Iterator可以称为是"fail-fast Iterator",意思是快速失败迭代器,就是检测到并发修改时,直接抛出异常,而不是继续执行,等到获取到一些错误值时在抛出异常。 + +异常检测主要是通过modCount和expectedModCount两个变量来实现的, + +- modCount +集合被修改的次数,一般是被集合(ArrayList之类的)持有,每次调用add(),remove()方法会导致modCount+1 + +- expectedModCount +期待的modCount,一般是被Iterator(ArrayList.iterator()方法返回的iterator对象)持有,一般在Iterator初始化时会赋初始值,在调用Iterator的remove()方法时会对expectedModCount进行更新。(可以看看上面的ArrayList.Itr源码) + +然后在Iterator调用next()遍历元素时,会调用checkForComodification()方法比较modCount和expectedModCount,不一致就抛出ConcurrentModificationException。 + +单线程操作Iterator不当时也会抛出ConcurrentModificationException异常。(上面的例子就是) + +#### 总结 + +因为ArrayList和HashMap的Iterator都是上面所说的“fail-fast Iterator”,Iterator在获取下一个元素,删除元素时,都会比较expectedModCount和modCount,不一致就会抛出异常。 + +所以当使用Iterator遍历元素(for-each遍历底层实现也是Iterator)时,需要删除元素,一定需要使用 **Iterator的remove()方法** 来删除,而不是直接调用ArrayList或HashMap自身的remove()方法,否则会导致Iterator中的expectedModCount没有及时更新,之后获取下一个元素或者删除元素时,expectedModCount和modCount不一致,然后抛出ConcurrentModificationException异常。 + +### 谈一谈你对LinkedHashMap的理解? + +LinkedHashMap是HashMap的子类,与HashMap的实现基本一致,只是说在HashMap的基础上做了一些扩展,所有的节点都有一个before指针和after指针,根据插入顺序形成一个**双向链表**。默认accessOrder是false,也就是按照**插入顺序**来排序的,每次新插入的元素都是插入到链表的末尾。map.keySet().iterator().next()第一个元素是最早插入的元素的key。LinkedHashMap可以用来实现LRU算法。(accessOrder为true,会按照访问顺序来排序。) + +![img](../static/249993-20161215143120620-1544337380-20201130113344624.png)LRU算法实现: + +```java +//使用LinkedHashMap实现LRU算法(accessOrder为false的实现方式) +// LinkedHashMap默认的accessOrder为false,也就是会按照插入顺序排序, +// 所以在插入新的键值对时,总是添加在队列尾部, +// 如果是访问已存在的键值对,或者是put操作的键值对已存在,那么需要将键值对先移除再添加。 +public class LRUCache{ + int capacity; + Map map; + public LRUCache(int capacity) { + this.capacity = capacity; + map = new LinkedHashMap<>(); + } + public int get(int key) { + if (!map.containsKey(key)) { return -1; } + //先删除旧的位置,再放入新位置 + Integer value = map.remove(key); + map.put(key, value); + return value; + } + public void put(int key, int value) { + if (map.containsKey(key)) { + map.remove(key); + map.put(key, value); + return; + } + //超出capacity,删除最久没用的,利用迭代器,删除第一个 + if (map.size() > capacity) { + map.remove(map.keySet().iterator().next()); + } + map.put(key, value); + } +} +``` +**下面是另外一种实现方法:** + +```java +//使用LinkedHashMap实现LRU算法(accessOrder为true的实现方式) +//如果是将accessOrder设置为true,get和put已有键值对时就不需要删除key了 +public static class LRUCache2 { + int capacity; + LinkedHashMap linkedHashMap; + LRUCache2(int capacity) { + this.capacity = capacity; + linkedHashMap = new LinkedHashMap(16,0.75f,true); + } + public int get(int key) { + Integer value = linkedHashMap.get(key); + return value == null ? -1 : value; + } + public void put(int key, int val) { + Integer value = linkedHashMap.get(key); + linkedHashMap.put(key, val); + if (linkedHashMap.size() > capacity) { + linkedHashMap.remove(linkedHashMap.keySet().iterator().next()); + } + } +} +``` +#### LinkedHashMap是怎么保存节点的插入顺序或者访问顺序的呢? +默认accessOrder为false,保存的是插入顺序,插入时调用的还是父类HashMap的putVal()方法,在putVal()中创建新节点时是会调用newNode()方法来创建一个节点,在newNode()方法中会调用linkNodeLast()方法将节点添加到双向链表的尾部。 +```java +final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { +//省略了部分代码... +tab[i] = newNode(hash, key, value, null); +//省略了部分代码... +} +//创建新节点 +Node newNode(int hash, K key, V value, Node e) { + LinkedHashMap.Entry p = new LinkedHashMap.Entry(hash, key, value, e); + linkNodeLast(p); + return p; +} +//移动到双向链表尾部 +private void linkNodeLast(LinkedHashMap.Entry p) { + LinkedHashMap.Entry last = tail; + tail = p; + if (last == null) head = p; + else { + p.before = last; + last.after = p; + } +} +``` + +如果accessOrder为true,会保存访问顺序,在访问节点时,会调用afterNodeAccess()方法将节点先从双向链表移除,然后添加到链表尾部。 +```java +public class LinkedHashMap + extends HashMap + implements Map +{ +//头结点,指向最老的元素 + transient LinkedHashMap.Entry head; +//尾节点,指向最新的元素 + transient LinkedHashMap.Entry tail; +//如果accssOrder为true,代表节点需要按照访问顺序排列,每次访问了元素,会将元素移动到尾部(代表最新的节点)。 +public V get(Object key) { + Node e; + if ((e = getNode(hash(key), key)) == null) + return null; + if (accessOrder) + afterNodeAccess(e); + return e.value; +} +//将节点从双向链表中删除,移动到尾部。 +void afterNodeAccess(Node e) { // move node to last + LinkedHashMap.Entry last; + if (accessOrder && (last = tail) != e) { + LinkedHashMap.Entry p = + (LinkedHashMap.Entry)e, + b = p.before, a = p.after; + p.after = null; + if (b == null) head = a; + else b.after = a; + if (a != null) a.before = b; + else last = b; + if (last == null) head = p; + else { + p.before = last; + last.after = p; + } + tail = p; + ++modCount; + } + } +} +``` + +### JDK1.7的HashMap链表成环是怎么造成的? + +这是JDK1.7里面HashMap扩容相关的代码: + +```java +void transfer(Entry[] newTable, boolean rehash) { + int newCapacity = newTable.length; + for (Entry e : table) {//遍历原数组的每一个下标 + while(null != e) { + //将数组下标下所有元素一个一个迁移到新数组,使用头插法 + Entry next = e.next; + if (rehash) { + e.hash = null == e.key ? 0 : hash(e.key); + } + int i = indexFor(e.hash, newCapacity); + e.next = newTable[i]; + newTable[i] = e; + e = next; + } + } +} +``` + +JDK1.7版本的HashMap中,两个线程同时添加一个新的键值对,然后同时触发扩容时,两个线程都会进行扩容,就会造成前一个线程将某个数组下标的元素迁移过去后,另一个线程又进行迁移。假设原数组下标下有node1->node2->null,这样一个链表,线程A和线程B都在迁移,都拿到了node1,假设线程A先执行,将两个节点迁移到新的数组,假设node1和node2在新数组还是在同一下标下,那么迁移后的链表是node2->node1->null,此时如果线程B还在迁移,拿到node1又迁移,会让node1-next=node,从而让node1和node2形成环。 + +##### 那么JDK1.8版本的HashMap是怎么解决的呢? + +首先迁移时不是拿到一个键值对就迁移一个了,而是对一个数组下标下的链表进行遍历,根据hash值的不同,分成两个链表,然后将两个链表分别设置到新的数组的下标下。 + +```java +if (oldTab != null) { + for (int j = 0; j < oldCap; ++j) { + Node e; + if ((e = oldTab[j]) != null) { + oldTab[j] = null; + if (e.next == null) + newTab[e.hash & (newCap - 1)] = e; + else if (e instanceof TreeNode) + ((TreeNode)e).split(this, newTab, j, oldCap); + else { // preserve order + Node loHead = null, loTail = null; + Node hiHead = null, hiTail = null; + Node next; + do { + next = e.next; + if ((e.hash & oldCap) == 0) { + if (loTail == null) + loHead = e; + else + loTail.next = e; + loTail = e; + } + else { + if (hiTail == null) + hiHead = e; + else + hiTail.next = e; + hiTail = e; + } + } while ((e = next) != null); + if (loTail != null) { + loTail.next = null; + newTab[j] = loHead; + } + if (hiTail != null) { + hiTail.next = null; + newTab[j + oldCap] = hiHead; + } + } + } + } + } +``` + + + +https://zhuanlan.zhihu.com/p/111501405 \ No newline at end of file diff --git a/docs/JVMBook.md b/docs/JVMBook.md new file mode 100644 index 0000000..2e9cfef --- /dev/null +++ b/docs/JVMBook.md @@ -0,0 +1,797 @@ +(PS:扫描首页里面的二维码进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +下面是主要是自己看了第三版的《深入理解Java虚拟机》后写的一些摘要,还没有进行整理,有点乱,之后会继续更新这一部分。 + + +## 第二章 Java内存区域与内存溢出异常 + +### 2.2运行时数据区域 + +运行时数据区域包含以下五个区域:程序计数器,Java虚拟机栈,本地方法栈,堆,方法区(其中前三个区域各线程私有,相互独立,后面两个区域所有线程共享) +![](../static/16f98e68c8fed611.png) + +### 线程私用的部分(Java虚拟机栈,本地方法栈,程序计数器) + +#### Java虚拟机栈 + +执行一个Java方法时,虚拟机都会创建一个栈帧,来存储局部变量表,操作数栈等,方法调用完毕后会对栈帧从虚拟机栈中移除。 + +局部变量表中存储了Java基本类型,对象引用(可以是对象的存储地址,也可以是代表对象的句柄等)和returnAddress类型(存储了一条字节码指令的地址)。 +#### 本地方法栈 + +本地方法栈与Java虚拟机栈类似,只不过是执行Native方法(C++方法等)。 + +#### 程序计数器 +计数器存储了当前线程正在执行的字节码指令的地址(如果是当前执行的是Native方法,那么计数器为空),字节码解释器就是通过改变计数器的值来选取下一条需要执行的字节码指令。程序计数器是线程私有的,便于各个线程切换后,可以恢复到正确的执行位置。 + +### 线程共享的部分(堆,方法区) +#### Java 堆 + +堆存储了几乎所有对象实例和数组,是被所有线程进行共享的区域。在逻辑上是连续的,在物理上可以是不连续的内存空间(在存储一些类似于数组的这种大对象时,基于简单和性能考虑会使用连续的内存空间)。 + +#### 方法区 +存储了被虚拟机加载的类型信息,常量,静态变量等数据,在JDK8以后,存储在元空间中(以前是存储在堆中的永久代中,JDK8以后已经没有永久代了)。 + +运行时常量池是方法区的一部分,会存储各种字面量和符号引用。具备动态性,运行时也可以添加新的常量入池(例如调用String的intern()方法时,如果常量池没有相应的字符串,会将它添加到常量池)。 + + +### 直接内存区(不属于虚拟机运行时数据区) +直接内存区不属于虚拟机运行时数据区的一部分。它指的是使用Native方法直接分配堆外内存,然后通过Java堆中的DirectByteBuffer来对内存的引用进行操作(可以避免Java堆与Native堆之间的数据复制,提升性能)。 + +### 2.3 HotSpot虚拟机对象探秘 +### 对象的创建过程 + +这是网上看到的一张流程图: + +![java对象创建流程](../static/20160505123459787.jpeg) + +#### 1.类加载检查 + +首先代码中new关键字在编译后,会生成一条字节码new指令,当虚拟机遇到一条字节码**new**指令时,会根据类名去方法区**运行时常量池**找类的**符号引用**,检查符号引用代表的类是否已加载,解析和初始化过。如果没有就执行相应的**类加载**过程。 + +#### 2.分配内存 + +虚拟机从Java堆中分配一块大小确定的内存(因为类加载时,创建一个此类的实例对象的所需的内存大小就确定了),并且初始化为零值。内存分配的方式有**指针碰撞**和**空闲列表**两种,取决于虚拟机采用的垃圾回收期是否带有空间压缩整理的功能。 + +##### 指针碰撞 + +如果垃圾收集器是Serial,ParNew等带有空间压缩整理的功能时,Java堆是规整的,此时通过移动内存分界点的指针,就可以分配空闲内存。 + +##### 空闲列表 + +如果垃圾收集器是CMS这种基于清除算法的收集器时,Java堆中的空闲内存和已使用内存是相互交错的,虚拟机会维护一个列表,记录哪些可用,哪些不可用,分配时从表中找到一块足够大的空闲内存分配给实例对象,并且更新表。 + +#### 3.对象初始化(虚拟机层面) +虚拟机会对对象进行必要的设置,将对象的一些信息存储在Obeject header 中。 + +#### 4.对象初始化(Java程序层面) +在构造一个类的实例对象时,遵循的原则是先静后动,先父后子,先变量,后代码块,构造器。在Java程序层面会依次进行以下操作: +* 初始化父类的静态变量(如果是首次使用此类) + +* 初始化子类的静态变量(如果是首次使用此类) + +* 执行父类的静态代码块(如果是首次使用此类) + +* 执行子类的静态代码块(如果是首次使用此类) + +* 初始化父类的实例变量 + +* 初始化子类的实例变量 + +* 执行父类的普通代码块 + +* 执行子类的普通代码块 + +* 执行父类的构造器 + +* 执行子类的构造器 + +#### PS:如何解决内存分配时的多线程并发竞争问题? + + 内存分配不是一个线程安全的操作,在多个线程进行内存分配是,可能会存在数据不同步的问题。所以有两种方法解决: + + ##### 添加CAS锁 + + 对内存分配的操作进行同步处理,添加CAS锁,配上失败重试的方式来保证原子性。(默认使用这种方式)。 + +##### 预先给各线程分配TLAB + + 预先在Java堆中给各个线程分配一块TLAB(本地线程缓冲区)内存,每个线程先在各自的缓冲区中分配内存,使用完了再通过第一种添加CAS锁的方式来分配内存。(是否启动取决于-XX:+/-UseTLAB参数)。 + +### 对象的内存布局是怎么样的? + +对象在内存中存储布局主要分为对象头,实例数据和对齐填充三部分。 + +这是网上看到的一张图: + +![5401975-4c082ac80e1c042c](../static/5401975-4c082ac80e1c042c.png) + +#### 对象头 + +对象头主要包含对象自身的运行时数据(也就是图中Mark Word),类型指针(图中的Class Pointer,指向对象所属的类)。如果对象是数组,还需要包含数组长度(否则无法确定数组对象的大小)。 + +**Mark Word**:存储对象自身的运行时数据,例如hashCode,GC分代年龄,锁状态标志,线程持有的锁等等。在32位系统占4字节,在64位系统中占8字节。 + + **Class Pointer**:用来指向对象对应的Class对象(其对应的元数据对象)的内存地址。在32位系统占4字节,在64位系统中占8字节。 + + **Length**:如果是数组对象,还有一个保存数组长度的空间,占4个字节。 + +#### 实例数据 + +保存对象的非静态成员变量数据。 + +#### 对齐填充 + +因为HotSpot虚拟机的自动内存管理系统要求对象起始地址是8字节的整数倍,所以任何对象的大小必须是8字节的整数倍,而对象头部分一般是8字节的倍数,如果实力数据部分不是8字节的整数倍,需要对齐填充来补全。 + +#### 对象的访问定位 + +根据对象的引用去访问的对象的方式有通过句柄访问和之间指针访问两种。 + +* 通过句柄访问 + + Java堆中有一块内存作为句柄池,每个句柄包含了对象的实例数据地址和类数据地址,对象的引用保存的是句柄地址,通过句柄再去访问对象。这种方式的好处是在垃圾回收时,移动对象时,对象的引用保存的地址不用变化,只需要改变句柄中保存的实例数据地址。 + +* 直接指针访问(HotSpot虚拟机的主要使用方式) + + 这种方式,对象引用保存的地址是对象的地址,根据对象引用可以直接访问对象,节省了一次指针定位的时间开销,速度更快。 + +## 第3章 垃圾收集器和内存分配策略 +因为程序计数器,虚拟机栈,本地方法栈都是线程私有的,生命周期与线程相同,当方法执行完或者线程结束时,内存就回收了,所以这三个内存区域的内存回收具备确定性,而堆和方法区的内存回收存在不确定性,所以需要垃圾收集器进行管理。 +### 垃圾回收的几种算法 +#### 引用计数法 +每个对象有一个引用计数,当其他对象引用这个对象时,该对象的引用计数+1,当其他对象不再引用这个对象时,引用计数-1。当引用计数为0时,垃圾回收器可以对对象进行回收。 + +缺点在于需要大量额外处理才能保证正确地工作,例如单纯的引用计数没有办法解决循环引用的问题。 + +#### 可达性分析算法 + +根据一系列GC Roots对象作为起始点,从这些节点根据引用关系向下搜索,走过的路径是引用链,如果一个对象没有被任何引用链相连,说明是不可达的,也就是不可能被使用的,会被判定为可回收的对象。 + + +GC Roots对象只有包括以下几种: + +* 虚拟机栈(栈帧中的本地变量表)引用的对象。例如方法的传参,局部变量等。 + +* 方法区中类的静态变量引用的对象。 + +* 方法区中常量引用的对象。例如字符串常量池中的引用。 + +* 本地方法栈中的引用的对象。 + +* Java虚拟机内部的引用,例如基本数据类型对应的Class对象,常驻的异常对象(NullPointException等) + +* 被同步锁(sychronized关键字修饰)持有的对象。 + + 除此以外,根据用户所选用的垃圾回收器以及当前回收的内存区域的不同,还可以将其他对象临时性加入,共同构成GC Roots集合,辅助内存回收。(例如回收新生代内存时,可以将一些关联区域的对象一并加入GC Roots集合中去。) + +### 对象的强引用,软引用,弱引用和虚引用 + +##### 强引用 + + 就是普通的变量对对象的引用,强引用的对象不会被系统回收。 + + ##### 软引用 + + 当内存空间足够时,软引用的对象不会被系统回收。当内存空间不足时,软引用的对象可能被系统回收。通常用于内存敏感的程序中。 + + ##### 弱引用 + + 引用级别比软引用低,对于只有软引用的对象,不管内存是否足够, 都可能会被系统回收。 + + ##### 虚引用 + + 虚引用主要用于跟踪对象被垃圾回收的状态,不能单独使用,必须和引用队列联合使用。 + +##### finalize()方法 + +第一次标记 + +垃圾回收器对对象进行可达性分析之后,发现没有任何于GCRoots连接的引用链引用着对象,会将对象进行**第一次标记**, + +第二次标记 + +* 如果对象有覆写finalize()方法 + + 会将对象添加到F-Queue队列中,让由一条虚拟机创建的、低调度优先级的线程去执行finalize()方法(不一定会等待运行结束,防止其他对象处于长时间的等待)。如果在finalize方法中,成功得让对象与引用链上的人格对象简历了关联,那么会被移除“即将回收”的集合,否则会被**第二次标记**。 + +* 如果对象没有覆写finalize()方法,或者finalize()方法已被调用,那么会对对象进行**第二次标记**,稍后对象会被回收。 + +#### 回收方法区 + +回收方法区主要是回收不再使用的常量和不再使用的类型。 + +不再使用的类型需要满足三个条件: + +* 类的所有的实例都已被回收。 +* 加载此类的类加载器已被回收。 +* 类的方法无法再通过反射调用。(类对应的java.lang.Class对象没有在任何地方被引用。) + +## 垃圾回收算法 + +**弱分代假说** + +绝对大多数对象都是朝生夕灭。 + +**强分代假说** + +熬过越多次垃圾收集过程的对象就越难以消灭。 + +基于这两个假说,Java虚拟机一般会分为新生代和老年代,然后根据区域内对象的特性,采用不通的垃圾回收算法。 + +**跨代引用假说** + +相比同代引用,跨代引用占极少数,所以不需要为了少量跨代引用,在回收新生代时去扫描整个老年代,只需要在新生代简历一个记忆集,将老年代化粪池多个小块,标识出哪一块内存存在跨代引用。 + +垃圾回收算法一般有四种 + +![1581500802565](../static/1581500802565.jpg) + +#### 标记-清除算法 + +就是对要回收的对象进行标记,标记完成后统一回收。(CMS垃圾收集器使用这种) + +缺点: + +**效率不稳定** + +执行效率不稳定,有大量对象时,并且有大量对象需要回收时,执行效率会降低。 + +**内存碎片化** + +内存碎片化,会产生大量不连续的内存碎片。(内存碎片只能通过使用分区空闲分配链表来分配内存) + +#### 标记-复制算法 + +就是将内存分为两块,每次只用其中一块,垃圾回收时将存活对象,拷贝到另一块内存。(serial new,parallel new和parallel scanvage垃圾收集器) + +缺点: + +**不适合存活率高的老年代** + +存活率较高时需要很多复制操作,效率会降低,所以老年代一般不使用这种算法。 + +**浪费内存** + +会浪费一半的内存, + +解决方案是新生代的内存配比是Eden:From Survivor: To Survivor = 8:1:1 + +每次使用时,Eden用来分配新的对象,From Survivor存放上次垃圾回收存活的对象,只使用Eden和From Survivor的空间,To Survivor是空的,垃圾回收时将存活对象拷贝到To Survivor,当空间不够时,从老年代进行分配担保。 + +####标记-整理算法 + +就是让存活对象往内存空间一端移动,然后直接清理掉边界以外的内存。(parallel Old和Serial old收集器就是采用该算法进行回收的) + +**吞吐量高** + +移动时内存操作会比较复杂,需要移动存活对象并且更新所有对象的引用,会是一种比较重的操作,但是如果不移动的话,会有内存碎片,内存分配时效率会变低,所以由于内存分配的频率会比垃圾回收的频率高很多,所以从吞吐量方面看,标记-整理法高于标记-清除法,所以强调高吞吐量的Parallel Scavenge收集器是采用标记整理法。 + +**延迟高** + +但是由于需要移动对象,停顿时间会比较长,垃圾回收时延迟会高一些,强调低延迟的CMS收集器一般是大部分时候用标记-清除算法,当内存碎片化程度达到一定程度时,触发Full GC,会使用标记-整理算法清理一次。 + +#### 分代收集算法 + +就是现在的系统一般都比较复杂,堆中的对象也会比较多,如果使用对所有对象都分析是否需要回收,那么效率会比较低,所以有了分代收集算法,就是对熬过垃圾回收次数不同的对象进行分类,分为新生代和老年代,采用不同回收策略。 + +新生代存活率低,使用标记-复制算法。新生代发生的垃圾收集交Minor GC,发生频率较高 + +老年代存活率高,使用标记-清除算法,或者标记-整理算法。(一般是多数时间采用标记-清除算法,内存碎片化程度较高时,使用标记-整理算法收集一次)。老年代内存满时会触发Major GC(Full GC),一般触发的频率比较低。 + +### HotSpot的算法实现 + +#### 枚举根节点 + +目前的垃圾收集器都是准确式的GC,就是当用户线程停顿下来时,进行垃圾回收时,不需要检查完所有的执行上下文(栈中的本地变量表)和全局性引用(常量,类静态变量)。在编译时,会通过一个OopMap数据结构在特定位置记录下栈和寄存器里哪些位置是引用。 + +#### 安全点 + +不可能给每条指令都生成OopMap,只能选定一些“可以让程序长时间运行”的指令作为安全点,在安全点生OopMap。安全点一般是指令会重复使用的指令,方法调用,循环跳转等。 + +垃圾收集时会让所有线程跑到最近的安全点去,然后停顿,实现方式有两种: + +抢先式中断:将所有用户线程中断,当一些线程中断的地方不是安全点时,恢复这些线程,执行到安全点,然后再中断。 + +主动式中断:线程在执行过程中会去轮询一个标志,当需要垃圾收集时,会对这个标志设置 + +,然后线程会执行到最近的安全点然后中断中断挂起。 + +##### 安全区域 + +在一个代码片段中,引用关系不会变化,那么在这个区域进行垃圾收集都是安全的。虚拟机进行垃圾收集时就不用管这些处于安全区域的线程,线程要离开安全区域时会检查虚拟机是否完成了根节点美剧,完成了,那么就继续执行,否则进行等待,直到收到可以离开安全区域的信号。(典型的场景是用户线程处于Sleep或者Block状态。) + +#### 记忆集 + +一种记录从非收集区域指向收集区域的指针集合的抽象数据结构。卡表就是记忆集的一种具体实现,卡表可以是一个字节数组,每个元素指向一个内存块,也就是卡页。卡页中通常包含很多个对象,当一个对象存在跨代指针时,就将对应卡表的数组元素值标记为1。垃圾回收时只会扫描这些标识为1的卡页。 + +#### 写屏障 + +写屏障可以看成是在虚拟机层面对引用类型字段赋值操作的AOP切面,就是虚拟机会对所有赋值操作生成相应的指令,在赋值前后对卡表进行更新。 + +##### 伪共享 + +CPU中的缓存系统是以缓存行为单位存储的,一个缓存行会包含多个变量,多线程修改相互独立的变量时,如果这些变量在共享一个缓存行,那么性能就会受影响。在更新卡表时为了避免伪共享问题,在对卡表元素进行标记时,如果已标记,就不进行标记操作了。 + +#### 并发可达性分析 + +在垃圾收集的过程中,用户线程可以和收集线程并发工作,垃圾收集器在进行根节点枚举时,会对对象进行标记: + +白色,未被扫描到(代表不可达) + +黑色,被扫描到,且对象所有的引用也被扫描到了(代表安全存活) + +灰色:对象正在被扫描(中间态) + +在扫描过程中,用户线程也会进行工作,可能会对引用进行更改,为了保持一致性有两种解决方案: + +原来是B引用C,垃圾收集过程中,去掉B对C的引用,改为A引用C + +增量更新,就是在更改引用关系时,将更改后的引用关系进行记录,扫描结束后对这些记录重新扫描。这个例子中会对A标记为灰色,进行记录,扫描结束后对A进行再扫描。 + +原始快照,就是在更改引用关系时,将原来的引用关系进行记录,扫码结束后对这些记录重新扫描。这个例子中会将原始的引用B-C记录下来,扫描结束后,重新扫描B。 + +#### 经典的垃圾收集器 + +[Java虚拟机(五)经典的垃圾收集器](http://antarctica.gitee.io/blog/2020/01/12/Java%E8%99%9A%E6%8B%9F%E6%9C%BA5/#%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8](http://antarctica.gitee.io/blog/2020/01/12/Java虚拟机5/#垃圾收集器) + +#### Serial收集器(标记-复制算法) + +就是最简单的垃圾收集器,也是目前 JVM 在 Client 模式默认的垃圾收集器,在进行垃圾收集时会停止用户线程,然后使用一个收集线程进行垃圾收集。主要用于新生代,使用标记-复制算法。 + +优点是简单高效(与其他收集器的单线程比),内存占用小,因为垃圾回收时就暂停所有用户线程,然后使用一个单线程进行垃圾回收,不用进行线程切换。 + +缺点是收集时必须停止其他用户线程。 + +#### Serial Old收集器(标记-整理算法) + +跟Serial收集器一样,不过是应用于老年代,使用标记-整理算法。 + +![image-20200228184419205](../static/image-20200228184419205.png) + +#### ParNew收集器(标记-复制算法) + +ParNew收集器是Serial收集器的多线程并行版本,在进行垃圾收集时可以使用多个线程进行垃圾收集。 + +与Serial收集器主要区别就是支持多线程收集,ParNew收集器应用广泛(JDK9以前,服务端模式垃圾收集组合官方推荐的是ParNew+CMS),因为只有Serial和ParNew才能配合CMS收集器(应用于老年代的并发收集器)一起工作。 + +![image-20200228185412716](../static/image-20200228185412716.png) + +#### Parallel Scanvenge收集器(吞吐量优先收集器) + +也支持多线程收集,它的目标是达到一个可控制的吞吐量,就是运行用户代码的时间/CPU消耗的总时间的比值。高吞吐量可以最高效率地利用处理器资源,尽快完成程序运算任务,适合不需要太多的交互分析任务。 + +#### Parallel Old收集器 + +是Parallel Scanvenge老年代版本,支持多线程收集,使用标记整理法实现的, + +![image-20200228191619466](../static/image-20200228191619466.png) + +#### CMS 收集器(老年代并发低停顿收集器) + +CMS收集器是第一个支持并发收集的垃圾收集器,在垃圾收集时,用户线程可以和收集线程一起工作,它的执行目标是达到最短回收停顿时间,以获得更好的用户体验。 + +CMS英文是Concurrent Mark Sweep,是基于标记-清除法实现的,步骤如下: + +初始标记:标记那些GC Roots可以直接关联到的对象。 + +并发标记:从GC Roots能关联到的对象直接向下遍历整个对象图,耗时较长,但是可以与用户线程并发执行。 + +重新标记:由于标记时,用户线程在运行,并发标记阶段存在一些标记变动的情况,这一阶段就是修正这些记录。(CMS采用增量更新算法来解决,主要是将更改后的引用关系记录,之后增量更新) + +并发清除:清除标记阶段判断的已经死亡的对象,由于不需要移动存活对象,这个阶段也可以与用户线程并发执行。 + +![image-20200228195544758](../static/image-20200228195544758.png) + +#### G1收集器(Garbage First收集器,标记-整理算法,老年代,新生代都进行回收) + +目标是在延迟可控(用户设定的延迟时间)的情况下获得尽可能高的吞吐量。 + +JDK9以前,服务端模式默认的收集器是Parallel Scavenge+Parallel Old,JDK9之后,默认收集器是G1。G1不按照新生代,老年代进行划分,而是将Java堆划分为多个大小相等的独立Region,每一个Region可以根据需要,扮演新生代的Eden空间,Survivor空间,老年代空间,回收思路是G1持续跟踪各个Region的回收价值(回收可释放的空间和回收所需时间),然后维护一个优先级列表,在用户设定的最大收集停顿时间内,优先回收那些价值大的Region。 + +Region如何解决跨代指针 + +使用记忆集,普通的记忆集只会标记那些卡表中那些卡页中存在对象被非收集区域引用,从而将这个卡页标记为脏,扫描时作为GC Roots扫描,这里的记忆集是一个哈希表,key是其他Region的起始地址,Value是一个集合,里面是卡表的索引号。 + +收集步骤 + +**初始标记** 只标记GC Roots直接引用的对象 + +**并发标记** 从GC Roots开始对堆中对象进行可达性分析,扫描整个对象图,找出要回收的对象。(可以与用户线程并发执行) + +**最终标记** 对并发标记阶段,由于用户线程执行造成的改动进行修正,使用原始快照方法。 + +**筛选回收** 对Region进行排序,根据回收价值,选择任意多个Region构成回收集,将存活对象复制到空的Region中去,因为涉及到存活对象的移动,所以是暂停用户线程的。 + + + +![image-20200302181639409](../static/image-20200302181639409.png) + + + +shenandoah和ZGC都是处于实验阶段的垃圾收集器 + +### 内存分配和回收策略 + +![image-20200303152150129](../static/image-20200303152150129.png) + +Serial收集器中, + +##### 新生代 + +分为Eden,From Survivor,To Survivor,8:1:1 + +Eden用来分配新对象,满了时会触发Minor GC。 + +From Survivor是上次Minor GC后存活的对象。 + +To Survivor是用于下次Minor GC时存放存活的对象。 + +##### 老年代 + +用于存放存活时间比较长的对象,大的对象,当容量满时会触发Major GC(Full GC) + +##### 1.对象优先在Eden分配 + +对象优先在Eden分配,Eden空间不够时触发Minor GC + +##### 2.大对象直接进入老年代 + +为了避免大对象在Eden和两个Survivor区之间进行来回复制,所以当对象超过-XX:+PrintTenuringDistribution参数设置的大小时,直接从老年代分配、 + +##### 3.长期存活的对象将进入老年代 + +当对象经历的Minor GC次数大于XX:MaxTenuringThreshold参数设置的次数时,在Minor GC时进入老年代(默认是15)。 + +##### 4.动态对象年龄判定 + +同年龄的对象大小总和超过Survivor区空间的一半时,年龄大于或等于该年龄的对象可以直接进入老年代。 + +##### 5.空间分配担保 + +在JDK6以后,只要老年代连续空间大于新生代对象总大小或者历次进入老年代的大小,就会进行Minor GC,否则进入Full GC(收集整个Java 堆和方法区的垃圾)。 + +### 类文件结构 + +每个Java文件对应一个类或者接口,编译后,转换成class文件, + +每个class文件由魔数CAFEBABE+class文件版本号(四字节)+常量池等等 + +class中数据结构分为两种 + +无符号数:也就是基本数据类型,可以描述数字,索引,数量值,字符串值等。以u1,u2,u4,u8分别代表1,2,4,8个字节 + +表:由多个无符号数,其他表作为数据项构成的复合数据类型 + +### 类加载 + +![img](https://pic.yupoo.com/crowhawk/2a1c6490/d926d9a2.png) + +将描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,变成虚拟机可以直接使用的java类型的过程。 + +类的生命周期是加载,验证,准备,解析(也有可能在初始化后执行),初始化,使用,卸载。 + +第一次用到某个类被主动引用时,一般是以下六种情况: + +1.遇到new(使用new构造对象),getstatic(读取静态变量),putstatic(设置静态变量),invokestatic(调用静态方法)四条字节码指令时。 + +2.使用java.lang.reflect包中的方法对类型进行发射调用。 + +3、初始化一个类时,其父类还没有初始化,触发父类的初始化。(接口初始化时,不需要接口的父类初始化,用到时才会初始化) + +4.虚拟机启动时,会初始化指定的主类。 + +5.JDK7之后的动态语言支持。 + +6.JDK8之后的默认方法(接口中被default修饰的方法),实现类初始化时,接口要在之前进行初始化。 + +当类是被动引用时,不会触发初始化: + +1.通过子类去调用父类的静态变量,SubClass.fatherStaticValue + +2.通过数组定义类引用类,SuperClass[] array = new SuperClass[10]; + +不会触发SuperClass类的初始化,但是执行字节码指令newarray会触发另外一个类[Lorg.fenixsoft.classloading.SuperClass的初始化,这个类继承于Object类,是一个包装类,里面包含了访问数组的所有方法, + +3.只引用类的常量不会触发初始化,因为常量在编译阶段进入常量池 + +```java +class SuperClass { + public static final String str = "hello"; +} + +//引用常量编译时会直接存入常量池 +System.out.println(SuperClass.str); + +``` + +#### 加载 + +类加载的第一步是加载,主要包含三个步骤: + +1.根据类的全名去获取类的二进制字节流(可以从class文件获取,也可也从网络获取,例例如Web Applet,也可以运行时生成,例如动态代理技术) + +2.将字节流所存储的静态存储结构转化为方法区的运行时数据结构。 + +3.从内存中生成一个代表此类的java.lang.Class对象,作为方法区中类的各种数据访问入口。 + +普通类的加载阶段可开发性很强,可以使用虚拟机内置的类加载器来完成,也可以使用自定义的类加载器去完成(重写类加载器的findClass()或loadClass()方法)。数组类不通过类加载器创建,是直接在内存中动态构造出来的。加载阶段可能会与连接阶段交叉进行,就是加载还没有完成时,连接阶段已经开始。 + +#### 验证 + +确保Class文件中的字节流包含的信息符合JAVA规范,不会危害到虚拟机的安全。 + +主要有 + +1.文件格式验证(字节流是不是魔数开头,主次版本号是否在可接受范围内) + +2.元数据验证(主要是语义分析,检查是否符合Java的语法,是否每个类都有父类什么的) + +3.字节码验证(确定程序语义是合法的,例如跳转指令不会跳转到方法体以外的字节指令上) + +4.符号引用验证(将符号引用转换为直接引用的过程,在解析阶段发生,为了保证正常解析,在这里对类的符号引用进行匹配性校验,例如找不到引用的外部类) + +#### 准备 + +为类的静态变量分配内存,赋初值的阶段。 + +```java +public static int value = 123;//准备阶段会给value赋初值0,在初始化阶段,执行()方法时才会赋值123 + +public static final int value = 123;//准备阶段就赋初值123 +``` + +#### 解析 + +解析不一定是在类加载时执行,也有可能是只会用到时才执行。就是将常量池中的符号引用替换为直接引用的过程。符号引用其实就是从Class文件中加载出来的字符串,本身没有跟真正的类,方法进行关联。主要是针对类,接口,字段,类方法,接口方法,方法类型,方法句柄。 + +https://www.zhihu.com/question/30300585 + +**符号引用**:用一组符号来描述所引用的目标,其实就是在Java中调用一个方法,转换为class字节码以后,是这样的 + +``` +CONSTANT_Methodref_info { + u1 tag;//tag代表常量池数据类型 + u2 class_index;//类索引 + u2 name_and_type_index;//方法名称索引 +} +``` + +例如调用X类的foo()方法中会调用bar()方法, +Java代码如下: +```java +public class X { + public void foo() { + bar(); + } + + public void bar() { } +} +``` +编译后的Class文件如下: +```java +Classfile /private/tmp/X.class + Last modified Jun 13, 2015; size 372 bytes + MD5 checksum 8abb9cbb66266e8bc3f5eeb35c3cc4dd + Compiled from "X.java" +public class X + SourceFile: "X.java" + minor version: 0 + major version: 51 + flags: ACC_PUBLIC, ACC_SUPER +Constant pool: + #1 = Methodref #4.#16 // java/lang/Object."":()V + #2 = Methodref #3.#17 // X.bar:()V + #3 = Class #18 // X + #4 = Class #19 // java/lang/Object + #5 = Utf8 + #6 = Utf8 ()V + #7 = Utf8 Code + #8 = Utf8 LineNumberTable + #9 = Utf8 LocalVariableTable + #10 = Utf8 this + #11 = Utf8 LX; + #12 = Utf8 foo + #13 = Utf8 bar + #14 = Utf8 SourceFile + #15 = Utf8 X.java + #16 = NameAndType #5:#6 // "":()V + #17 = NameAndType #13:#6 // bar:()V + #18 = Utf8 X + #19 = Utf8 java/lang/Object +{ + public X(); + flags: ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #1 // Method java/lang/Object."":()V + 4: return + LineNumberTable: + line 1: 0 + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this LX; + + public void foo(); + flags: ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #2 // Method bar:()V + 4: return + LineNumberTable: + line 3: 0 + line 4: 4 + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this LX; + + public void bar(); + flags: ACC_PUBLIC + Code: + stack=0, locals=1, args_size=1 + 0: return + LineNumberTable: + line 6: 0 + LocalVariableTable: + Start Length Slot Name Signature + 0 1 0 this LX; +} +``` +java代码中的` bar();`转换为字节码后是 +`[B6] [00 02]`翻译过来其实是`invokevirtual #2` +B6是invokevitual指令的操作码,02是常量池中下标为2的常量项。 +而下标为2的常量项是`#3.#17`,也就是`X.bar:()V`,这就是符号引用,解析后会转换为直接引用,就是虚方法表的下标和参数个数, +`[B6] [00 02]` 翻译结果`invokevirtual #2` +转换为直接引用后就是 +`[D6] [06] [01]`翻译结果invokevirtual_quick vtable_index=6, args_size=1,也就是执行虚方法表里面偏移量为6的方法,参数个数为1(每个实例方法都有一个隐藏参数,也就是当前对象)。 +所以JVM在执行invokevirtual_quick要调用X.bar()时,只要顺着对象引用查找到虚方法表,然后从中取出第6项的methodblock\*,就可以找到实际应该调用的目标然后调用过去了。 + +**直接引用**:可以直接指向目标的指针,相对偏移量或者是句柄。 + +**方法解析**的过程:首先根据类索引class_index来找到所属的类,然后查找方法,如果又找到,那么就返回直接引用,找不到就去父类里面找,找到了返回直接引用。如果还没有找到就去实现的接口和父接口里面找,找到了说明类C是个抽象类,抛出AbstractMethodError异常,否则查找失败,抛出NoSuchMethodError。 + +大致过程是:找到类->从当前类中->从父类中找->从实现的接口,父接口里面找 + +**字段解析** + +大致过程是:找到类->从当前类中->从接口中找->从父类中找 + +当某个同名字段同时出现在父类,接口中,会编译时报错,实际上解析是可以按顺序解析的。 + +#### 初始化 + +初始化的过程其实就是执行类构造器 loadClass(String name) throws ClassNotFoundException { + return loadClass(name, false); + } + protected Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + synchronized (getClassLoadingLock(name)) { + Class c = findLoadedClass(name); + if (c == null) { + ... + try { + if (parent != null) { + c = parent.loadClass(name, false); + } else { + c = findBootstrapClassOrNull(name); + } + } catch (ClassNotFoundException e) { + } + + if (c == null) { + ... + c = findClass(name); + // do some stats + ... + } + } + if (resolve) { + resolveClass(c); + } + return c; + } + } + protected Class findClass(String name) throws ClassNotFoundException { + throw new ClassNotFoundException(name); + } + ... +} +``` + +### 怎么自定义一个类加载器? + +加载一个类时,一般是调用类加载器的loadClass()方法来加载一个类,loadClass()方法的工作流程如下: + +1.先调用findLoadedClass(className)来获取这个类,判断类是否已加载。 + +2.如果未加载,如果父类加载器不为空,调用父类加载器的loadClass()来加载这个类,父类加载器为空,就调用父类加载器加载这个类。 + +3.父类加载器加载失败,那么调用该类加载器findClass(className)方法来加载这个类。 + +所以我们我们一般自定义类加载器都是继承ClassLoader,来重新findClass()方法,来实现类加载。 + +```java +public class DelegationClassLoader extends ClassLoader { + private String classpath; + + public DelegationClassLoader(String classpath, ClassLoader parent) { + super(parent); + this.classpath = classpath; + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + InputStream is = null; + try { + String classFilePath = this.classpath + name.replace(".", "/") + ".class"; + is = new FileInputStream(classFilePath); + byte[] buf = new byte[is.available()]; + is.read(buf); + return defineClass(name, buf, 0, buf.length); + } catch (IOException e) { + throw new ClassNotFoundException(name); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + throw new IOError(e); + } + } + } + } + + public static void main(String[] args) + throws ClassNotFoundException, IllegalAccessException, InstantiationException, + MalformedURLException { + sun.applet.Main main1 = new sun.applet.Main(); + + DelegationClassLoader cl = new DelegationClassLoader("java-study/target/classes/", + getSystemClassLoader()); + String name = "sun.applet.Main"; + Class clz = cl.loadClass(name); + Object main2 = clz.newInstance(); + + System.out.println("main1 class: " + main1.getClass()); + System.out.println("main2 class: " + main2.getClass()); + System.out.println("main1 classloader: " + main1.getClass().getClassLoader()); + System.out.println("main2 classloader: " + main2.getClass().getClassLoader()); + ClassLoader itrCl = cl; + while (itrCl != null) { + System.out.println(itrCl); + itrCl = itrCl.getParent(); + } + } +} +``` \ No newline at end of file diff --git a/docs/JavaBasic.md b/docs/JavaBasic.md new file mode 100644 index 0000000..195c533 --- /dev/null +++ b/docs/JavaBasic.md @@ -0,0 +1,728 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +下面是主要是自己看了《疯狂Java讲义》后,学习到一些之前没掌握的技术点,写的解答,之后会继续更新和完善。 + +#### [1.Java中的多态是什么?](#Java中的多态是什么?) + +#### [2.Java中变量,代码块,构造器之间执行顺序是怎么样的?](#java中变量,代码块,构造器之间执行顺序是怎么样的?) +#### [3.final关键字有哪些作用?](#final关键字有哪些作用?) + +#### [4.Integer类会进行缓存吗?](#Integer类会进行缓存吗?) + +#### [5.抽象类有哪些特点?](#抽象类有哪些特点?) + +#### [6.String,StringBuffer和StringBuilder之间的区别是什么?](#String,StringBuffer和StringBuilder之间的区别是什么?) + +#### [7.编译型编程语言,解释型编程语言,伪编译型语言的区别是什么?](#编译型编程语言,解释型编程语言,伪编译型语言的区别是什么?) + +#### [8.Java中的访问控制符有哪些?](#Java中的访问控制符有哪些?) + +#### [9.Java的构造器有哪些特点?](#Java的构造器有哪些特点?) + +#### [10.Java中的内部类是怎么样的?](#Java中的内部类是怎么样的?) + +#### [11.Java中的注解是什么?](#Java中的注解是什么) + +#### [12.为什么hashCode()和equal()方法要一起重写?](#为什么hashCode()和equal()方法要一起重写?) + +#### [13.Java中有哪些数据类型?](#Java中有哪些数据类型?) + +#### [14.包装类型和基本类型的区别是什么?](#包装类型和基本类型的区别是什么?) + + +### Java中的多态是什么? + +**多态**指的是相同类型的变量在调用同一个方法时呈现出多种**不同的行为特征**。而造成这一现象的原因在于Java中的变量有两个类型: + +* 编译时类型,由声明变量时的类型决定。 + +* 运行时类型,由实际赋值给变量的对象的类型决定, + + 当一个变量的两个类型不一致,就会出现多态。 + +```java +//BaseClass是SubClass的父类 +BaseClass a = new BaseClass(); +BaseClass b = new SubClass(); +a.baseMethod()//变量a调用baseMethod()方法,实际上会调用BaseClass的baseMethod()方法,会打印111 +b.baseMethod()//变量b调用baseMethod方法,实际上会调用SubClass的重写baseMethod()方法,会打印222 +Class BaseClass { + void baseMethod() { + System.out.println("111"); + } +} +Class SubClass { + void baseMethod() { + System.out.println("222"); + } +} +输出结果: +111 +222 +``` +例如这个例子中, + +##### 对于变量a而言 + +a的**编译类型是BaseClass**,**实际类型也是BaseClass**,所以调用baseMethod()会执行BaseClass#baseMethod()方法,打印出111。 + +##### 对于变量b而言 + +b的的**编译类型是BaseClass**,但是实际赋值时,给变量b赋值的是SubClass对象,所以b的**实际类型是SubClass**。而SubClass重写了父类BaseClass#baseMethod()方法,所以调用baseMethod()方法会调用SubClass#baseMethod(),从而打印出222。 + +a和b的编译类型相同,却展现出了不同的行为特征,这就是多态。 + +(PS:如果直接对b调用只有SubClass有的方法,编译时会报错,但是可以通过反射进行调用。) + +### Java中变量,代码块,构造器之间执行顺序是怎么样的? + +Java程序中类中个元素的初始化顺序 +初始化的原则是: + +* 先初始化**静态**部分,再初始化**动态**部分, + +* 先初始化**父类**部分,后初始化**子类**部分, + +* 先初始化**变量**,再初始化**代码块**和**构造器**。 + +所以依照这个规则可以得出总体顺序是: + +1.父类的静态成员变量(如果是第一次加载类) + +2.父类的静态代码块(如果是第一次加载类) + +3.子类的静态成员变量(如果是第一次加载类) + +4.子类的静态代码块(如果是第一次加载类) + +5.父类的普通成员变量 + +6.父类的动态代码块 + +7.父类的构造器方法 + +8.子类的普通成员变量 + +9.子类的动态代码块 + +10.子类的构造器方法 + +下面写了一个Demo进行验证: +```java +public class Base { + static Instance staticInstance = new Instance("1---Base类的静态成员变量staticInstance"); + static { + System.out.println("2---Base类的静态代码块执行了"); + } + Instance instance = new Instance("5---Base类的普通成员变量instance"); + { + System.out.println("6---Base类的动态代码块执行了"); + } + Base() { + System.out.println("7---Base类的构造器方法执行了"); + } +} + +public class Child extends Base { + static Instance staticInstance = new Instance("3---Child类的静态成员变量staticInstance"); + static { + System.out.println("4---Child类的静态代码块执行了"); + } + Instance instance = new Instance("8---Child类的普通成员变量instance"); + { + System.out.println("9----Child类的动态代码块执行了"); + } + Child() { + System.out.println("10---Child类的构造器方法执行了"); + } + public static void main(String[] args) { + Child child = new Child(); + } +} +``` + +输出结果如下: + +``` +1---Base类的静态成员变量staticInstance进行了初始化 +2---Base类的静态代码块执行了 +3---Child类的静态成员变量staticInstance进行了初始化 +4---Child类的静态代码块执行了 +5---Base类的普通成员变量instance进行了初始化 +6---Base类的动态代码块执行了 +7---Base类的构造器方法执行了 +8---Child类的普通成员变量instance进行了初始化 +9----Child类的动态代码块执行了 +10---Child类的构造器方法执行了 +``` + +说明确实是按照上面的执行顺序执行的。 + +### final关键字有哪些作用? + +##### 修饰类 + +final修饰类时,类不能被继承,并且类中的所有方法都被隐式地使用final修饰。 + +##### 修饰方法 + +final修饰方法时,有两个作用: + +* 方法不能被子类重写。 +* 可以让方法转换为内联调用,提升效率。(早期的Java版本是这样,最近的版本已经没有这些优化了,因为如果方法过大时,内嵌调用无法带来性能提升。) + +* 修饰变量 + + final修饰的变量一旦初始化,就不能被修改。当final修饰实例变量时,可以在在定义变量时赋初值,也可以在定义时不赋值,在动态代码块,或构造器中赋初值。(只能赋值一次)。 + + 如果final变量在声明时就指定了初始值,并且在编译时可以确定下来,那么在编译时,final变量本质上会变成一个宏变量,所有用到该变量的地方都直接替换成该变量的值。 + +### Integer类会进行缓存吗? + +```java +Intger a = new Integer(127); +Intger b = Interger.valueOf(127); +Intger c = Interger.valueOf(127); +Intger d = Interger.valueOf(128); +Intger e = Interger.valueOf(128); +System.out.println(a == b); //输出false +System.out.println(b == c); //输出true +System.out.println(d == e); //输出false +``` + +上面这种例子的原因是如果 + +* 通过new Interger()创建Interger对象,每次返回全新的Integer对象 + +* 通过Interger.valueOf()创建Interger对象,如果值在-128到127之间,会返回缓存的对象(初始化时)。 + + ##### 实现原理 + + Integer类中有一个静态内部类IntegerCach,在加载Integer类时会同时加载IntegerCache类,IntegerCache类的静态代码块中会创建值为-128到127的Integer对象,缓存到cache数组中,之后调用Integer#valueOf方法时,判断使用有缓存的Integer对象,有则返回,无则调用new Integer()创建。 + + + + PS:(127是默认的边界值,也可以通过设置JVM参数java.lang.Integer.IntegerCache.high来进行自定义。) + +```java +public static Integer valueOf(int i) { + if (i >= IntegerCache.low && i <= IntegerCache.high) + //cache是一个Integer数组,缓存了-128到127的Integer对象 + return IntegerCache.cache[i + (-IntegerCache.low)]; + return new Integer(i); +} + +//IntegerCache是Integer类中的静态内部类,Integer类在加载时会同时加载IntegerCache类,IntegerCache类的静态代码块中会 +private static class IntegerCache { + static final int low = -128; + static final int high; + static final Integer cache[]; + + static { + // 127是默认的边界值,也可以通过设置JVM参数java.lang.Integer.IntegerCache.high来进行自定义。 + int h = 127; + String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); + if (integerCacheHighPropValue != null) { + try { + int i = parseInt(integerCacheHighPropValue); + i = Math.max(i, 127); + // cache数组的长度不能超过Integer.MAX_VALUE + h = Math.min(i, Integer.MAX_VALUE - (-low) -1); + } catch( NumberFormatException nfe) { + // If the property cannot be parsed into an int, ignore it. + } + } + high = h; + + cache = new Integer[(high - low) + 1]; + int j = low; + for(int k = 0; k < cache.length; k++) + cache[k] = new Integer(j++); + + // high值必须大于等于127,不然会抛出异常 + assert IntegerCache.high >= 127; + } + + private IntegerCache() {} + } +``` + +### 抽象类有哪些特点? + +从多个具体的类中抽象出来的父类叫做抽象类,抽象类就像是一个模板。抽象类使用abstract关键字修饰,抽象类可以拥有抽象方法(使用abstract修饰的方法,由子类来提供方法的实现)。抽象类的特点如下: + +1.可以包含抽象方法 + +抽象方法与普通方法的区别在于,抽象类不提供抽象方法的实现,由继承抽象类的子类实现抽象方法。 + +2.不能实例化 + +也正因为包含抽象方法,抽象类不能被实例化。抽象类的构造器不能用于创建实例,是仅提供给子类调用的。除此以外,抽象类跟普通类一样,可以拥有成员变量,普通方法,构造器,初始化块,内部类,枚举等。 + +3.抽象类可以被继承 + +继承抽象类的子类如果实现了所有抽象方法,那么可以作为普通类使用,否则也只能作为抽象类。 + +### String,StringBuffer和StringBuilder之间的区别是什么? + +##### 1.可变性 + +String是一个不可变类,任何对String改变都是会产生一个新的String对象,所以String类是使用final来进行修饰的。而StringBuffer和StringBuilder是可变类,对应的字符串的改变不会产生新的对象。 + +##### 2.执行效率 + +当频繁对字符串进行修改时,使用String会生成一些临时对象,多一些附加操作,执行效率降低。 + +```java +stringA = StringA + "2"; +//实际上等价于 +{ + StringBuffer buffer = new StringBuffer(stringA) + buffer.append("2"); + return buffer.toString(); +} +``` + +在对stringA进行修改时,实际上是先根据字符串创建一个StringBuffer对象,然后调用append()方法对字符串修改,再调用toString()返回一个字符串。 + +##### 3.线程安全 + +StringBuffer的读写方法都使用了synchronized修饰,同一时间只有一个线程进行操作,所以是线程安全的,而StringBuilder不是线程安全的。 + +### Object类有哪些自带方法? + +#### registerNatives() + +首先Object类有一个本地方法registerNatives(),会在类加载时调用,主要是将Object类中的一些本地方法绑定到指定的函数中去,便于之后调用。例如将hashCode和clone本地方法绑定到JVM_IHashCode和JVM_IHashCode函数。 + +```java + private static native void registerNatives(); + static { + registerNatives(); + } +``` + +想深入了解的朋友可以看看这两篇文章: + +[Java之Thread源码registerNatives()深入理解](https://juejin.im/post/5dbb959151882523b5402c8f) + +[Object类中的registerNatives方法的作用深入介绍](https://blog.csdn.net/Saintyyu/article/details/90452826) + +#### getClass() + +**getClass()**方法会返回对象的**运行时**类。 + +```java + public final native Class getClass(); +``` + +具体可以看下面这个例子: + +有一个类Son,继承于Father类,instance的编译类型是Father,实际赋值的是一个Son对象,所以调用instance.getClass()获取的是instance对象的运行时类型,打印的类名是"com.test.Son"。 + +```java + package com.test; + class Father {} + + class Son extends Father {} + + Father instance = new Son(); + Class class1 = instance.getClass(); + System.out.println("class1 is"class1); +``` +输出结果: +```java +class1 is class com.test.Son +``` +##### Classs.forName() +与**getClass()**类似的方法还有两个,一个是Class类中的**forName()**方法,也是在**运行时**,根据传入的类名去加载类,然后返回与类关联的Class对象。也正是因为是动态加载,在编译时可以没有这个类,也不会对类名进行检验,所以有可能抛出ClassNotFoundException异常。 + +```java +public static Class forName(String className) + throws ClassNotFoundException { + Class caller = Reflection.getCallerClass(); + return forName0(className, true, ClassLoader.getClassLoader(caller), caller); +} +``` +可以自己尝试运行一下这段代码 +```java + Class class2 = null; + try { + class2 = Class.forName("com.test.Son");//如果实际不存在com.test.Son这个类,那么会抛出ClassNotFoundException异常 + System.out.println(class2); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } +``` + +```java +class2 is class com.test.Son +``` +##### 类名+.class + +还有一种方式是使用**类名+.class**来获取类关联的Class对象,与上面两种方法不同之处在于,它返回的是编译时类型,也就是在编译时,类加载器将类加载到JVM中,然后初始化一个Class对象返回。 +```java + Class class3 = Son.class; + System.out.println(class3); +``` +输出结果: +```java + class3 is class com.test.Son +``` + +PS:无论是使用哪种方式来获取类关联的Class对象,类都是只会加载一次,如果获取Class对象时,不会重复加载类。 + +### 为什么hashCode()和equals()方法要一起重写? +#### hashCode()和equals()方法 + +可以看到Obejct类中的源码如下,可以看到equals()方法的默认实现是判断两个对象的内存地址是否相同来决定返回结果。 + +```java + public native int hashCode(); + + public boolean equals(Object obj) { + return (this == obj); + } +``` + +网上很多博客说hashCode的默认实现是返回内存地址,其实不对,以OpenJDK为例,hashCode的默认计算方法有5种,有返回随机数的,有返回内存地址,具体采用哪一种计算方法取决于运行时库和JVM的具体实现。(可以理解Object类hashCode默认实现是对所有对象返回的值都不一样。) + +感兴趣的朋友可以看看这篇博客 + +[Java的Object.hashCode()的返回值到底是不是对象内存地址?](https://blog.csdn.net/xusiwei1236/article/details/45152201) + +##### hashCode()方法的作用有哪些? + +* 对对象做散列 + + 为了将一组键值对均匀得存储在一个数组中,HashMap对key的hashCode进行计算得到一个hash值,用hash对数组长度取模,得到数组下标,将键值对存储在数组下标对应的链表下。 + +* 快速判断对象是否不相等 + + 因为两个对象hashCode相等,调用equals()方法的结果不一定为true, + + 因为两个对象调用equals()方法相等,hashCode一定相等。 + + 所以hashCode不相等可以作为两个对象不相等的快速判断条件。 + + 在往HashMap中添加一个键值对时,计算得到数组下标后,会遍历数组下标下存储的链表中,拿key的hashCode与每个节点的hashCode进行比较,相等时,才调用equals()方法进行继续调用,节约时间。(在一些类的equal()方法的自定义实现中也会对hashCode进行判断)。 + +##### 假如只重写hashCode()方法(结果:HashMap可以存在两个内存地址不相同,但是相等的对象,无法保证去重) + +此时equals()方法的实现是默认实现,也就是当两个对象的内存地址相等时,equals()方法才返回true,假设两个键值对,它们的key类型都是TestObject,的值都是test,但是由于是使用new String()创建而成的字符串对象,key1和key2的内存地址不相等,所以key1==key2的结果会是false,TestObject的equals()方法默认实现是判断两个对象的内存地址,所以 key1.equals(key2)也会是false, 所以两个键值对可以重复地添加到hashMap中去。 + +```java +public class TestObject { + Integer a; + public TestObject(Integer a) { + this.a = a; + } + @Override + public int hashCode() { + return a; + } + + public static void main(String[] args) { + TestObject key1 = new TestObject(1); + TestObject key2 = new TestObject(1); + System.out.println("key1的hashCode为"+ key1 +"key2的hashCode为" + key2); + System.out.println("key1.equals(key2)的结果为"+(key1.equals(key2))); + + HashMap map = new HashMap(); + map.put(key1,"value1"); + map.put(key2,"value2"); + System.out.println("HashMap是"+map.toString()); + } +} +``` + +输出结果: + +```java +key1的hashCode为com.test.TestObject@1 +key2的hashCode为com.test.TestObject@1 + +key1.equals(key2)的结果为false + +HashMap是 +{com.test.TestObject@1=value1, +com.test.TestObject@1=value2} +``` + +##### 假如只重写equals()方法(结果:相同的对象hashCode不同,从而映射到不同下标下,HashMap无法保证去重) + +假设只equals()方法,hashCode方法会是默认实现,具体的计算方法取决于JVM,可能会导致两个相等的对象,它们的hashCode却不相同,从而计算得到的数组下标不相同,存储到hashMap中不同数组下标下的链表中,也会导致HashMap中存在重复元素。 + +```java +public class TestObject { + Integer a; + public TestObject(Integer a) { + this.a = a; + } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TestObject that = (TestObject) o; + return Objects.equals(a, that.a); + } + + public static void main(String[] args) { + TestObject key1 = new TestObject(1); + TestObject key2 = new TestObject(1); + System.out.println("key1的hashCode为"+ key1 +"key2的hashCode为" + key2); + System.out.println("key1.equals(key2)的结果为"+(key1.equals(key2))); + + HashMap map = new HashMap(); + map.put(key1,"value1"); + map.put(key2,"value2"); + System.out.println("HashMap是"+map.toString()); + } +} +``` + +输出结果如下: + +```java +key1的hashCode为1288141870 +key2的hashCode为2054881392 + +key1.equals(key2)的结果为true + +HashMap是 +{com.test.TestObject@4cc77c2e=value1, com.test.TestObject@7a7b0070=value2} +``` + +#### clone()方法 + +clone()方法会创建并返回当前对象的副本。副本与原对象的区别在于它们相等,但是存储在不同的内存位置中。 + +```java +protected native Object clone() throws CloneNotSupportedException; +``` + +要调用clone()方法必须实现Cloneable接口,否则调用默认的Object类的clone方法会抛出CloneNotSupportedException异常。默认clone()方法返回的对象是浅拷贝的。 + +#### toString()方法 + +返回类名+@+hashCode的16进制字符串 + +```java +public String toString() { + return getClass().getName() + "@" + Integer.toHexString(hashCode()); +} +``` + +#### wait()方法和notify()方法 + +```java +//timeout是超时时间,也就是等待的最大毫秒数,如果为0,代表会一直等待下去 +public final native void wait(long timeout) throws InterruptedException; +public final native void notify(); +public final native void notifyAll(); +``` + +##### wait() + +wait()方法可以让当前线程放弃对象的监视器(可以简单认为监视器就是一个锁),进入等待队列,进行等待,直到其他线程调用notify()或者notifyAll()后(或者过了超时时间),线程才会从等待队列,移动到同步队列,再次获得对象的监视器后才能继续执行。 + +##### notify() + +notify()可以唤醒等待队列中的某一个线程,线程被唤醒后会从等待队列移动到同步队列,线程再次获得对象的监视器后才能继续执行。(然后调用notify()方法的线程会继续执行,在同步块中执行完毕后,会释放对象的监视器。) + +##### notifyAll() + +notifyAll()方法与notify()方法类似,只是会将等待队列中的所有线程唤醒。 + +可以看看这张图。 + +![4](../static/4.png) + +#### finalize + +``` +//默认为空的实现,子类可以重写这个方法 +protected void finalize() throws Throwable { } +``` + +当垃圾回收器确认某个对象不被任何其他对象引用时(即对象处于可恢复状态),系统在回收对象时,会调用finalize()方法,可以在这个方法中清理资源,在这个方法中,也有可能让对象重新获得引用,从而变成可达状态。 + +### 编译型编程语言,解释型编程语言,伪编译型语言的区别是什么? + +* 编译型编程语言 + + 指的是用专门的编译器,对于针对特定平台将某种高级语言编写的源代码一次性编译成可在该平台硬件执行的机器码,并打包成该平台所能识别的可执行性程序的格式。优点是执行效率比较高,缺点是无法跨平台运行。需要移植到其他平台上运行,需要采用特定平台的编译器重新编译。C,C++,Objective-C,Swift,Kotlin都属于此类。 + +* 解释型编程语言 + + 使用特定的解释器对源代码进行逐行解释并立即执行的的语言。缺点是因为每次执行都需进行编译,运行效率低,而且不能脱离解释器独立运行,优点是可以跨平台运行,只要在特定平台上提供特定的解释器就行了。JavaScript,Ruby,Python都属于此类。 + +* 伪编译型语言 + + 编译时只编译成中间代码,将中间代码和解释器一起打包成可执行文件。然后执行时使用解释器将中间代码解析成二进制代码。Visual Basic属于此类。 + +Java + Java是通过javac编译器将源代码编译成跟平台无关的字节码(也就是.class文件),然后由不同平台上的 JVM(Java虚拟机)对字节码解释执行,在一些Java虚拟机的实现中,还会将字节码转换为特定系统的机器码,提高执行效率。Java语言不属于上面的任何一类,因为目前的高级语言较为复杂,已经不能简单地以编译型编程语言,解释型编程语言,伪编译型语言进行划分了。 + +### Java中的访问控制符有哪些? + +在Java中使用访问控制符修饰成员变量,方法,构造方法时 + +| 访问范围 | private | default | protected | public | +| -------------- | ------- | ------- | --------- | ------ | +| 本类中 | 允许 | 允许 | 允许 | 允许 | +| 同一包中 | | 允许 | 允许 | 允许 | +| 其他包中的子类 | | | 允许 | 允许 | +| 其他包中 | | | | 允许 | + +private 允许在类中访问。 + +default 允许在类中,同一包中访问。 + +protected 允许在类中,同一包中,其他包中的子类 访问。 + +public 只允许在所有地方访问。 + +##### 注意事项: + +如果某个类Father一个方法A是没有使用访问修饰符,那么子类Son如果是在其他包中,不能调用这个方法A。但是如果方法A是使用protected修饰的,那么在子类中可以调用。(但是不能使用父类去调用,就是不能在子类中去创建父类对象,然后用父类对象去调用。) + +具体可以看看下面这个例子: + +```java +package com.one; +//假设有一个Father类在包com.one下 +public class Father { + void defalutTest() {} + protected void protectedMothod() {} +} + +package com.two; +import com.one.Father; +public class Son extends Father { + public static void main(String[] args) { + Father father = new Father(); + father.protectedMothod()//位置1.这句代码会报错,因为即便是protected修饰的方法,在其他包中的子类中,也不能用父类去调用,只能按位置2或者位置4那样去调用 + + Son son = new Son(); + son.protectedMothod();//位置2 + } + void someMethod() { + this.defaultMethod();//位置3.这句代码会报错,因为默认的方法不允许在其他包中的子类调用的 + this.protectedMothod();//位置4.可以调用成功 + } +} +``` + +### Java的构造器有哪些特点? + +```java +public class Test { + String str; + public Test(String str) { + this.str = str; + } +} +``` +1.如果没有自定义构造器,系统会提供一个默认的无参数构造器。如果提供了自定义的构造器,系统就不会提供默认的无参数构造器。(也就是不能直接调用new Test()来创建一个对象了,除非自己自定义一个无参数构造器)。 + +2.在上面的代码中,其实在构造器Test(String str)调用之前,系统已经分配好空间,创建一个对象,然后执行构造器Test(String str)方法对对象进行初始化,然后返回。 + +3.构造器一般使用public修饰符修饰,也可以使用protected,private来限制访问。 + +4.构造器重载,指的是一个类可以有多个构造器,多个构造器的参数列表不同。 + +### Java中的内部类是怎么样的? + +内部类分为静态内部类和非静态内部类。静态内部类是与外部类相关的,而非静态内部类是与外部类的实例对象相关的。 + +##### 静态内部类 + +静态内部类一般使用public static修饰,也可以使用private static使用,那样只能在外部类内部使用。在外部类以外的地方使用静态类时,需要带上外部类的包名,例如创建一个静态内部类对象: + +```java +OutClass.InnerClass object = new OutClass.InnerClass(); +``` + +##### 非静态内部类 + +非静态内部类是跟外部类的实例对象绑定在一起的。外部类一般是由两种访问修饰符default(只能包内访问),public(所有位置可以访问)。而非静态内部类有private,default,protected,public四种访问修饰符。因为必须跟外部类的实例对象绑定在一起,所以非静态内部类不能有静态方法,静态成员变量,静态初始化块,在外面创建一个非静态内部类对象: + +```java +OutClass out = new OutClass(); +OutClass.InnerClass object = out.new InnerClass(); +``` +### Java中的注解是什么? +Java中的注解其实是继承于annotation接口的一个接口,根据@Retention修饰的作用范围,注解可以是作用于源码层面,字节码文件层面,运行时层面。 + +```java +// 如果@Retention的值是RetentionPolicy.Source 那么在编译时注解就失效了,不会编译进class文件 +// 如果@Retention的值是RetentionPolicy.CLASS 那么会编译进class文件,但是JVM加载class文件时就会丢弃这个注解,在运行时注解就失效了 +// 如果@Retention的值是RetentionPolicy.RUNTIME 在运行时注解同样有效 +``` + +根据@Retention的值,注解主要分为编译时扫描和运行时扫描,例如@Override注解是作用于源码层面的,只是在编译时,编译器会去检验method是否是真正重写了父类的某个方法。 + +```java +@Override +public void method() { + +} + + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface Override { + +} +``` + +除了几个JDK自带的注解以外,通常情况下,我们使用的注解都是运行时注解,在运行时,JVM在运行时会针对注解生成一个动态代理类,通过反射获取注解时,实际上返回的是Java运行时生成的动态代理对象$Proxy1,而Proxy类就是我们注解(接口)的具体实现类。 + +### Java 异常体系是怎么样的? + +![img](../static/1415794-20190804110605330-45276489.png) + +Throwable的子类为Error和Exception + +Exception的子类为RuntimeException异常和RuntimeException以外的异常(例如IOException)。 + +主要分为Error,RuntimeException类及其子类的非受检异常和非受检异常以外的受检异常。 + +Error就是一些程序处理不了的错误,代表JVM出现了一些错误,应用程序无法处理。例如当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。 + +RuntimeException异常就是应用程序运行时,可能会抛出的异常。这些异常是非受检异常,编译时Java编译器不会去检查,不会强制程序员添加处理异常的代码。程序中可以选择捕获处理,也可以不处理。如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等。 + +非受检异常以外的异常可以认为是受检异常,从程序语法角度讲是必须进行处理的异常,编译时编译器就会要求有相关的异常捕获处理的代码逻辑。如IOException、SQLException,FileNotFoundException。 + +##### PS: + +@Transaction默认检测异常为RuntimeException及其子类,如果有其他异常需要回滚事务的需要自己手动配置,例如:@Transactional(rollbackFor = Exception.class) + +### Java中有哪些基本数据类型? + + +| 类型 | 字节数 | 取值范围 | +| ------- | -------------------------------------------------- | ------------------------------------------------------------ | +| byte | 1字节 | -128~127,也就是-2的7次方到2的7次方减1 | +| short | 2字节 | -32768~32767,就是-2的15次方到2的15次方减1 | +| int | 4字节 | -2147483648~2147483647,-2的31次方到2的31次方减1,换算成十进制应该是有10位,也就是十亿的数量级。 | +| long | 8字节 | -2的63次方到2的64次方,换算成十进制的数,是有19位。 | +| boolean | 理论上1字节就可以满足需求,为了内存对齐一般是4字节 | | +| float | 4字节 | | +| double | 8字节 | | +| char | 2字节 | C语言的char类型其实是1字节,使用anscil编码,取值范围是0~127,有一个2进制位作为数据校验位。Java使用的是unicode编码,16个二进制位。取值范围会大一些,所以用的2字节,可以存汉字。 | + +### 包装类型和基本类型的区别是什么? + +最主要的区别是包装类型是对象,拥有字段和方法,可以很方便地调用一些基本的方法,初始值是null,而且可以使用null代表空值,而基本数据类型只能使用0来代表初始值。其次是基本数据类型是直接存储在栈中,而包装类型是一个对象,对象的引用变量是存储在栈中,存储了对象在堆中的地址,对象的数据是存在堆中。 + +### JDK8 Stream与普通for循环的区别? + +对于简单操作,比如最简单的遍历,Stream串行api性能明显差于显示迭代,但并行的Stream API能够发挥多核特性。 + +元素为整型时,普通for循环耗时:串行api耗时:双核CPU并行Stream API=1:2:1 + +元素为字符串型时,普通for循环耗时:串行api耗时:双核CPU并行Stream API=1:1.5:0.72。 + +https://blog.csdn.net/zhenghongcs/article/details/104305798 \ No newline at end of file diff --git a/docs/JavaJVM.md b/docs/JavaJVM.md new file mode 100644 index 0000000..fbf2eec --- /dev/null +++ b/docs/JavaJVM.md @@ -0,0 +1,1298 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +目前还只是买了最新版的[《深入理解JVM虚拟机 第三版》](backend/bookRecommend?#《深入理解Java虚拟机-第三版》),还没有完全看完,看完之后会从网上的面经中找一些实际的面试题,然后自己通过翻书查资料,写面试题解答。 + +#### [1.Java内存区域怎么划分的?](#Java内存区域怎么划分的?) + +#### [2.Java中对象的创建过程是怎么样的?](#Java中对象的创建过程是怎么样的?) +#### [3.Java对象的内存布局是怎么样的?](#Java对象的内存布局是怎么样的?) +#### [4.垃圾回收有哪些特点?](#垃圾回收有哪些特点?) +#### [5.在垃圾回收机制中,对象在内存中的状态有哪几种?](#在垃圾回收机制中,对象在内存中的状态有哪几种?) +#### [6.对象的强引用,软引用,弱引用和虚引用的区别是什么?](#对象的强引用,软引用,弱引用和虚引用的区别是什么?) +#### [7.双亲委派机制是什么?](#双亲委派机制是什么?) +#### [8.怎么自定义一个类加载器?](#怎么自定义一个类加载器?) +#### [9.垃圾回收算法有哪些?](#垃圾回收算法有哪些?) +#### [10.Minor GC和Full GC是什么?](#MinorGC和FullGC是什么?) +#### [11.如何确定一个对象可以回收?](#如何确定一个对象是否可以被回收?) +#### [12.目前通常使用的是什么垃圾收集器?](#目前通常使用的是什么垃圾收集器?) + + +### Java内存区域怎么划分的? +运行时数据区域包含以下五个区域:程序计数器,Java虚拟机栈,本地方法栈,堆,方法区(其中前三个区域各线程私有,相互独立,后面两个区域所有线程共享) +![](../static/16f98e68c8fed611.png) + +### 线程私用的部分(Java虚拟机栈,本地方法栈,程序计数器) + +#### Java虚拟机栈 + +执行一个Java方法时,虚拟机都会创建一个栈帧,来存储局部变量表,操作数栈等,方法调用完毕后会对栈帧从虚拟机栈中移除。 + +局部变量表中存储了Java基本类型,对象引用(可以是对象的存储地址,也可以是代表对象的句柄等)和returnAddress类型(存储了一条字节码指令的地址)。 +#### 本地方法栈 + +本地方法栈与Java虚拟机栈类似,只不过是执行Native方法(C++方法等)。 + +#### 程序计数器 +计数器存储了当前线程正在执行的字节码指令的地址(如果是当前执行的是Native方法,那么计数器为空),字节码解释器就是通过改变计数器的值来选取下一条需要执行的字节码指令。程序计数器是线程私有的,便于各个线程切换后,可以恢复到正确的执行位置。 + +### 线程共享的部分(堆,方法区) + +#### Java 堆 + +堆存储了几乎所有对象实例和数组,是被所有线程进行共享的区域。在逻辑上是连续的,在物理上可以是不连续的内存空间(在存储一些类似于数组的这种大对象时,基于简单和性能考虑会使用连续的内存空间)。 + +#### 方法区 +存储了被虚拟机加载的**类型信息**,**常量**,**静态变量**等数据,在JDK8以后,存储在**方法区的元空间**中(以前是存储在堆中的永久代中,JDK8以后已经没有永久代了)。 + +**运行时常量池**是方法区的一部分,会存储各种字面量和符号引用。具备动态性,运行时也可以添加新的常量入池(例如调用String的intern()方法时,如果常量池没有相应的字符串,会将它添加到常量池)。 + + +### 直接内存区(不属于虚拟机运行时数据区) +直接内存区不属于虚拟机运行时数据区的一部分。它指的是使用Native方法直接分配堆外内存,然后通过Java堆中的DirectByteBuffer来对内存的引用进行操作(可以避免Java堆与Native堆之间的数据复制,提升性能)。 + +### Java中对象的创建过程是怎么样的? + +这是网上看到的一张流程图: + +![java对象创建流程](../static/20160505123459787.jpeg) + +#### 1.类加载检查 + +首先代码中new关键字在编译后,会生成一条字节码new指令,当虚拟机遇到一条字节码**new**指令时,会根据类名去方法区**运行时常量池**找类的**符号引用**,检查符号引用代表的类是否已加载,解析和初始化过。如果没有就执行相应的**类加载**过程。 + +#### 2.分配内存 + +虚拟机从Java堆中分配一块大小确定的内存(因为类加载时,创建一个此类的实例对象的所需的内存大小就确定了),并且初始化为零值。内存分配的方式有**指针碰撞**和**空闲列表**两种,取决于虚拟机采用的垃圾回收期是否带有空间压缩整理的功能。 + +##### 指针碰撞 + +如果垃圾收集器是Serial,ParNew等带有空间压缩整理的功能时,Java堆是规整的,此时通过移动内存分界点的指针,就可以分配空闲内存。 + +##### 空闲列表 + +如果垃圾收集器是CMS这种基于清除算法的收集器时,Java堆中的空闲内存和已使用内存是相互交错的,虚拟机会维护一个列表,记录哪些可用,哪些不可用,分配时从表中找到一块足够大的空闲内存分配给实例对象,并且更新表。 + +#### 3.对象初始化(虚拟机层面) +虚拟机会对对象进行必要的设置,将对象的一些信息存储在Obeject header 中。 + +#### 4.对象初始化(Java程序层面) +在构造一个类的实例对象时,遵循的原则是先静后动,先父后子,先变量,后代码块,构造器。在Java程序层面会依次进行以下操作: +* 初始化父类的静态变量(如果是首次使用此类) + +* 初始化子类的静态变量(如果是首次使用此类) + +* 执行父类的静态代码块(如果是首次使用此类) + +* 执行子类的静态代码块(如果是首次使用此类) + +* 初始化父类的实例变量 + +* 初始化子类的实例变量 + +* 执行父类的普通代码块 + +* 执行子类的普通代码块 + +* 执行父类的构造器 + +* 执行子类的构造器 + +#### PS:如何解决内存分配时的多线程并发竞争问题? + + 内存分配不是一个线程安全的操作,在多个线程进行内存分配是,可能会存在数据不同步的问题。所以有两种方法解决: + + ##### 添加CAS锁 + +对内存分配的操作进行同步处理,添加CAS锁,配上失败重试的方式来保证原子性。(默认使用这种方式)。 + +##### 预先给各线程分配TLAB + +预先在Java堆中给各个线程分配一块TLAB(本地线程缓冲区)内存,每个线程先在各自的缓冲区中分配内存,使用完了再通过第一种添加CAS锁的方式来分配内存。(是否启动取决于-XX:+/-UseTLAB参数)。 + +### Java对象的内存布局是怎么样的? + +对象在内存中存储布局主要分为对象头,实例数据和对齐填充三部分。 + +这是网上看到的一张图: + +![5401975-4c082ac80e1c042c](../static/5401975-4c082ac80e1c042c.png) + +#### 对象头 + +对象头主要包含对象自身的**运行时数据**(也就是图中Mark Word),**类型指针**(图中的Class Pointer,指向对象所属的类)。如果对象是数组,还需要包含数组长度(否则无法确定数组对象的大小)。 + +**Mark Word**:存储对象自身的运行时数据,例如hashCode,GC分代年龄,锁状态标志,线程持有的锁等等。在32位系统占4字节,在64位系统中占8字节。 + +![在这里插入图片描述](../static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5NDY4NTcz,size_16,color_FFFFFF,t_70.jpeg) + + **Class Pointer**:用来指向对象对应的Class对象(其对应的元数据对象)的内存地址。在开启了指针压缩时,占4字节。(默认是开启的) + + **Length**:如果是数组对象,还有一个保存数组长度的空间,占4个字节。 + +#### 实例数据 + +保存对象的非静态成员变量数据。实例数据存储的是真正的有效数据,即各个字段的值。无论是子类中定义的,还是从父类继承下来的都需要记录。这部分数据的存储顺序受到虚拟机的分配策略以及字段在类中的定义顺序的影响。 + +#### 对齐填充 + +因为HotSpot虚拟机的自动内存管理系统要求对象起始地址是8字节的整数倍,所以任何对象的大小必须是8字节的整数倍,而对象头部分一般是8字节的倍数,如果实力数据部分不是8字节的整数倍,需要对齐填充来补全。 + +### 如何计算一个对象在内存中占多少个字节? + +由于默认是开启了指针压缩的,现在普遍的机器位数都是64位,如果对象是普通对象,非数组类型,那么就是对象头部分就是12字节(类型指针4字节,Mark Word 8字节),假设这个对象只有一个Integer变量,那么在实例数据部分就是一个引用变量的空间4字节,总共是16字节,由于正好是8的倍数,就不需要进行对齐填充了。 + + +### 垃圾回收有哪些特点? + +垃圾回收具有以下特点: + +1.只回收堆内存的对象,不回收其他物理资源(数据库连接等) + +2.无法精准控制内存回收的时机,系统会在合适的时候进行内存回收。 + +3.在回收对象之前会调用对象的finalize()方法清理资源,这个方法有可能会让其他变量重新引用对象导致对象复活。 + +### 在垃圾回收机制中,对象在内存中的状态有哪几种? + +1.**可达状态** + +有一个及以上的变量引用着对象。 + +2.**可恢复状态** + +已经没有变量引用对象了,但是还没有被调用finalize()方法。系统在回收前会调用finalize()方法,如果在执行finalize()方法时,重新让一个变量引用了对象,那么对象会变成可达状态,否则会变成不可达状态。 + +3.**不可达状态** + +执行finalize()方法后,对象还是没有被其他变量引用,那么对象就变成了不可达状态。 + +### 对象的强引用,软引用,弱引用和虚引用的区别是什么? + +##### 强引用 + +就是普通的变量对对象的引用,强引用的对象不会被系统回收。 + +##### 软引用 + +当内存空间足够时,软引用的对象不会被系统回收。当内存空间不足时,软引用的对象可能被系统回收。通常用于内存敏感的程序中。 + +##### 弱引用 + +引用级别比软引用低,对于只有软引用的对象,不管内存是否足够,在垃圾回收时, 都可能会被系统回收。 + +##### 虚引用 + +虚引用主要用于跟踪对象被垃圾回收的状态,在垃圾回收时可以收到一个通知。 + +### 双亲委派机制是什么? + +![img](../static/4491294-8edc15f60a58bd0b.png) + +就是类加载器一共有三种: + +**启动类加载器**:主要是在加载JAVA_HOME/lib目录下的特定名称jar包,例如rt.jar包,像java.lang就在这个jar包中。 + +**扩展类加载器**:主要是加载JAVA_HOME/lib/ext目录下的具备通用性的类库。 + +**应用程序类加载器**:加载用户类路径下所有的类库,也就是程序中默认的类加载器。 + +工作流程: + +除启动类加载器以外,所有类加载器都有自己的父类加载器,类加载器收到一个类加载请求时,首先会判断类是否已经加载过了,没有的话会调用父类加载器的的loadClass方法,将请求委派为父加载器,当父类加载器无法完成类加载请求时,子加载器才尝试去加载这个类。 +目的是为了保证每个类只加载一次,并且是由特定的类加载器进行加载(都是首先让启动类来进行加载)。 + +```java +public abstract class ClassLoader { + ... + public Class<?> loadClass(String name) throws ClassNotFoundException { + return loadClass(name, false); + } + protected Class<?> loadClass(String name, boolean resolve) + throws ClassNotFoundException { + //使用synchronized加锁,保证不会重复加载 + synchronized (getClassLoadingLock(name)) { + //判断这个类是否已加载 + Class<?> c = findLoadedClass(name); + if (c == null) { + ... + try { + if (parent != null) {//有父类加载器,让父类加载器先尝试加载 + c = parent.loadClass(name, false); + } else { + c = findBootstrapClassOrNull(name); + } + } catch (ClassNotFoundException e) { + } + //使用当前类加载器进行类加载 + if (c == null) { + ... + c = findClass(name); + // do some stats + ... + } + } + if (resolve) { + resolveClass(c); + } + return c; + } + } + protected Class<?> findClass(String name) throws ClassNotFoundException { + throw new ClassNotFoundException(name); + } + ... +} +``` + +### 怎么自定义一个类加载器? + +加载一个类时,一般是调用类加载器的loadClass()方法来加载一个类,loadClass()方法的工作流程如下: + +1.先调用findLoadedClass(className)来获取这个类,判断类是否已加载。 + +2.如果未加载,如果父类加载器不为空,调用父类加载器的loadClass()来加载这个类,父类加载器为空,就调用父类加载器加载这个类。 + +3.父类加载器加载失败,那么调用该类加载器findClass(className)方法来加载这个类。 + +所以我们一般自定义类加载器都是**继承ClassLoader,重写findClass()方法**,来实现类加载,这样不会违背双亲委派类加载机制,也可以通过重写loadClass()方法进行类加载,但是这样会违背双亲委派类加载机制。 + +```java +public class DelegationClassLoader extends ClassLoader { + private String classpath; + + public DelegationClassLoader(String classpath, ClassLoader parent) { + super(parent); + this.classpath = classpath; + } + + @Override + protected Class<?> findClass(String name) throws ClassNotFoundException { + InputStream is = null; + try { + String classFilePath = this.classpath + name.replace(".", "/") + ".class"; + is = new FileInputStream(classFilePath); + byte[] buf = new byte[is.available()]; + is.read(buf); + return defineClass(name, buf, 0, buf.length); + } catch (IOException e) { + throw new ClassNotFoundException(name); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + throw new IOError(e); + } + } + } + } + + public static void main(String[] args) + throws ClassNotFoundException, IllegalAccessException, InstantiationException, + MalformedURLException { + sun.applet.Main main1 = new sun.applet.Main(); + + DelegationClassLoader cl = new DelegationClassLoader("java-study/target/classes/", + getSystemClassLoader()); + String name = "sun.applet.Main"; + Class<?> clz = cl.loadClass(name); + Object main2 = clz.newInstance(); + + System.out.println("main1 class: " + main1.getClass()); + System.out.println("main2 class: " + main2.getClass()); + System.out.println("main1 classloader: " + main1.getClass().getClassLoader()); + System.out.println("main2 classloader: " + main2.getClass().getClassLoader()); + ClassLoader itrCl = cl; + while (itrCl != null) { + System.out.println(itrCl); + itrCl = itrCl.getParent(); + } + } +} +``` + +### 类加载的过程是什么样的? + +#### 类加载器 + +类加载器是 Java 运行时环境(Java Runtime Environment)的一部分,负责动态加载 Java 类到 Java 虚拟机的内存空间中。**类通常是按需加载,即第一次使用该类时才加载。** 由于有了类加载器,Java 运行时系统不需要知道文件与文件系统。每个 Java 类必须由某个类加载器装入到内存。 + +![jvm_classloader_architecture](../static/jvm_classlaoder_architecture.svg) + +类装载器除了要定位和导入二进制 class 文件外,还必须负责验证被导入类的正确性,为变量分配初始化内存,以及帮助解析符号引用。这些动作必须严格按一下顺序完成: + +1. **装载**:查找并装载类型的二进制数据。 +2. **链接**:执行验证、准备以及解析(可选) - + -**验证**:确保被导入类型的正确性 + -**准备**:为类变量分配内存,并将其初始化为默认值。 + - **解析**:把类型中的符号引用转换为直接引用。 +3. **初始化**:把类变量初始化为正确的初始值。 + +#### 装载 + +##### 类加载器分类 + +在Java虚拟机中存在多个类装载器,Java应用程序可以使用两种类装载器: + +- **Bootstrap ClassLoader**:此装载器是 Java 虚拟机实现的一部分。由原生代码(如C语言)编写,不继承自 `java.lang.ClassLoader` 。负责加载核心 Java 库,启动类装载器通常使用某种默认的方式从本地磁盘中加载类,包括 Java API。 +- **Extention Classloader**:用来在`/jre/lib/ext` ,或 `java.ext.dirs` 中指明的目录中加载 Java 的扩展库。 Java 虚拟机的实现会提供一个扩展库目录。 +- **Application Classloader**:根据 Java应用程序的类路径( `java.class.path` 或 `CLASSPATH` 环境变量)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 `ClassLoader.getSystemClassLoader()` 来获取它。 +- **自定义类加载器**:可以通过继承 `java.lang.ClassLoader` 类的方式实现自己的类加载器,以满足一些特殊的需求而不需要完全了解 Java 虚拟机的类加载的细节。 + +##### 全盘负责双亲委托机制 + +在一个 JVM 系统中,至少有 3 种类加载器,那么这些类加载器如何配合工作?在 JVM 种类加载器通过 **全盘负责双亲委托机制** 来协调类加载器。 + +- **全盘负责**:指当一个 `ClassLoader` 装载一个类的时,除非显式地使用另一个 `ClassLoader` ,该类所依赖及引用的类也由这个 `ClassLoader` 载入。 +- **双亲委托机制**:指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。 + +全盘负责双亲委托机制只是 Java 推荐的机制,并不是强制的机制。实现自己的类加载器时,如果想保持双亲委派模型,就应该重写 `findClass(name)` 方法;如果想破坏双亲委派模型,可以重写 `loadClass(name)` 方法。 + +##### 装载入口 + +所有Java虚拟机实现必须在每个类或接口首次主动使用时初始化。以下六种情况符合主动使用的要求: + +- 当创建某个类的新实例时(new、反射、克隆、序列化) +- 调用某个类的静态方法 +- 使用某个类或接口的静态字段,或对该字段赋值(用final修饰的静态字段除外,它被初始化为一个编译时常量表达式) +- 当调用Java API的某些反射方法时。 +- 初始化某个类的子类时。 +- 当虚拟机启动时被标明为启动类的类。 + +除以上六种情况,所有其他使用Java类型的方式都是被动的,它们不会导致Java类型的初始化。 +当类是被动引用时,不会触发初始化: + +1.通过子类去调用父类的静态变量,不会触发子类的初始化,只会触发包含这个静态变量的类初始化,例如执行这样的代码`SubClass.fatherStaticValue`只会触发FatherClass的初始化,不会触发SubClass的初始化,因为fatherStaticValue是FatherClass的变量 + +2.通过数组定义类引用类,SuperClass[] array = new SuperClass[10]; + +不会触发SuperClass类的初始化,但是执行字节码指令newarray会触发另外一个类[Lorg.fenixsoft.classloading.SuperClass的初始化,这个类继承于Object类,是一个包装类,里面包含了访问数组的所有方法, + +3.只引用类的常量不会触发初始化,因为常量在编译阶段进入常量池 + +```java +class SuperClass { + public static final String str = "hello"; +} + +//引用常量编译时会直接存入常量池 +System.out.println(SuperClass.str); + +``` + +对于接口来说,只有在某个接口声明的非常量字段被使用时,该接口才会初始化,而不会因为事先这个接口的子接口或类要初始化而被初始化。 + +**父类需要在子类初始化之前被初始化**。当实现了接口的类被初始化的时候,不需要初始化父接口。然而,当实现了父接口的子类(或者是扩展了父接口的子接口)被装载时,父接口也要被装载。(只是被装载,没有初始化) + +#### 验证 + +确认装载后的类型符合Java语言的语义,并且不会危及虚拟机的完整性。 + +- **装载时验证**:检查二进制数据以确保数据全部是预期格式、确保除 Object 之外的每个类都有父类、确保该类的所有父类都已经被装载。 +- **正式验证阶段**:检查 final 类不能有子类、确保 final 方法不被覆盖、确保在类型和超类型之间没有不兼容的方法声明(比如拥有两个名字相同的方法,参数在数量、顺序、类型上都相同,但返回类型不同)。 +- **符号引用的验证**:当虚拟机搜寻一个被符号引用的元素(类型、字段或方法)时,必须首先确认该元素存在。如果虚拟机发现元素存在,则必须进一步检查引用类型有访问该元素的权限。 + +#### 准备 + +在准备阶段,Java虚拟机为类变量分配内存,**设置默认初始值**。但在到到初始化阶段之前,类变量都没有被初始化为真正的初始值。 + +| 类型 | 默认值 | +| --------- | -------- | +| int | 0 | +| long | 0L | +| short | (short)0 | +| char | ’\u0000’ | +| byte | (byte)0 | +| blooean | false | +| float | 0.0f | +| double | 0.0d | +| reference | null | + +#### 解析 + +https://www.zhihu.com/question/30300585 + +解析的过程就是在类型的常量池总寻找类、接口、字段和方法的符号引用,**把这些符号引用替换为直接引用的过程**。 + +- `类或接口的解析`:判断所要转化成的直接引用是数组类型,还是普通的对象类型的引用,从而进行不同的解析。 +- `字段解析`:对字段进行解析时,会先在本类中查找是否包含有简单名称和字段描述符都与目标相匹配的字段,如果有,则查找结束;如果没有,则会按照继承关系从上往下递归搜索该类所实现的各个接口和它们的父接口,还没有,则按照继承关系从上往下递归搜索其父类,直至查找结束, + +#### 初始化 + +**所有的类变量(即静态量)初始化语句和类型的静态初始化器都被Java编译器收集在一起,放到一个特殊的方法中,这个步骤就是初始化类静态变量和执行静态代码块。** 对于类来说,这个方法被称作类初始化方法;对于接口来说,它被称为接口初始化方法。在类和接口的 class 文件中,这个方法被称为``。 + +1. 如果存在直接父类,且直接父类没有被初始化,先初始化直接父类。 +2. 如果类存在一个类初始化方法,执行此方法。 + +这个步骤是递归执行的,即第一个初始化的类一定是`Object`。 + +**Java虚拟机必须确保初始化过程被正确地同步。** 如果多个线程需要初始化一个类,仅仅允许一个线程来进行初始化,其他线程需等待。 + +> 这个特性可以用来写单例模式。 + +##### Clinit 方法 + +- 对于静态变量和静态初始化语句来说:执行的顺序和它们在类或接口中出现的顺序有关。 +- **并非所有的类都需要在它们的class文件中拥有()方法,** 如果类没有声明任何类变量,也没有静态初始化语句,那么它就不会有`()`方法。如果类声明了类变量,但没有明确的使用类变量初始化语句或者静态代码块来初始化它们,也不会有`()`方法。如果类仅包含静态`final`常量的类变量初始化语句,而且这些类变量初始化语句采用编译时常量表达式,类也不会有`()`方法。**只有那些需要执行Java代码来赋值的类才会有()** +- `final`常量:Java虚拟机在使用它们的任何类的常量池或字节码中直接存放的是它们表示的常量值。 + + +### JVM 内存区域分布是什么样的?gc 发生在哪些部分? + +Java虚拟机的内存区域主要分为**虚拟机栈**,**本地方法栈**,**程序计数器**,堆,**方法区**。垃圾回收主要是对**堆**中的对象进行回收,方法区里面也会进行一些垃圾回收,但是方法区中的内存回收出现的频率会比较低,主要是针对**常量池的回收** 和 **对类型的卸载**,判定一个常量是否是“废弃常量”比较简单,不被引用就行了,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面3个条件才能算是“无用的类”: + +1.该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例; + +2.加载该类的ClassLoader已经被回收; + +3.该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法; + +##### 虚拟机栈 + +是Java方法执行的内存模型,主要是用于方法执行的,每次执行方法时就会创建一个栈帧来压入栈中,每个栈帧存储了方法调用需要的数据,例如局部变量表,操作数栈等。 + +局部变量表主要是存储方法的参数和创建的局部变量(基本类型或者引用类型),它的大小在编译时就固定了,方法有一个Code属性记录了需要的局部变量表的大小。 + +操作数栈是一个栈,主要用来做算术运算及调用其他方法时存储传参,调用其他方法时,当前方法栈帧的操作数栈会跟其他方法的局部变量表有一定的重叠,主要便于共享这些传递参数,节约内存空间。(最大深度也是编译时就计算好的。) + +##### 本地方法栈 + +线程在调用native方法时使用的栈。 + +##### 程序计数器 + +跟虚拟机栈,本地方法栈一样,程序计数器也是线程私有的,主要用来记录当前线程执行的字节码指令的行号,字节码解释器通过改变这个计数器来选取下一条需要执行的字节码指令。 + +##### 堆 + +用来存储对象和数据的区域,被所有线程共享,在物理上可以是不连续的。 + +##### 方法区 + +方法区也是被线程共享的,主要存放类型信息,常量,静态变量,方法去中有一个运行时常量池。 + +Java中的类在编译后会生成class文件,class文件除了包含变量,方法,接口信息外还包含一个常量池表,用于存放编译时生成的各种字面量和符号引用,在类加载后,字符流常量池会被存放在方法区中的运行时常量池中,运行期间也会可以将新的常量添加到运行时常量池,例如对String的实例调用intern方法。 + +### 垃圾回收算法有哪些? + +垃圾回收算法一般有四种 + +![1581500802565](../static/1581500802565.jpg) + +#### 标记-清除算法(一般用于老年代) + +就是对要回收的对象进行标记,标记完成后统一回收。(CMS垃圾收集器使用这种) + +缺点: + +**效率不稳定** + +执行效率不稳定,有大量对象时,并且有大量对象需要回收时,执行效率会降低。 + +**内存碎片化** + +内存碎片化,会产生大量不连续的内存碎片。(内存碎片只能通过使用分区空闲分配链表来分配内存) + +#### 标记-复制算法(一般用于新生代) + +就是将内存分为两块,每次只用其中一块,垃圾回收时将存活对象,拷贝到另一块内存。(serial new,parallel new和parallel scanvage垃圾收集器) + +缺点: + +**不适合存活率高的老年代** + +存活率较高时需要很多复制操作,效率会降低,所以老年代一般不使用这种算法。 + +**浪费内存** + +会浪费一半的内存, + +解决方案是新生代的内存配比是Eden:From Survivor: To Survivor = 8比1比1 + +每次使用时,Eden用来分配新的对象,From Survivor存放上次垃圾回收后存活的对象,只使用Eden和From Survivor的空间,To Survivor是空的,垃圾回收时将存活对象拷贝到To Survivor,当空间不够时,从老年代进行分配担保。 + +#### 标记-整理算法(一般用于老年代) + +标记-整理算法跟标记-清除算法适用的场景是一样的,都是用于老年代,也就是存活对象比较多的情况。标记-整理算法的流程就是让存活对象往内存空间一端移动,然后直接清理掉边界以外的内存。(parallel Old和Serial old收集器就是采用该算法进行回收的) + +**吞吐量高** + +移动时内存操作会比较复杂,需要移动存活对象并且更新所有对象的引用,会是一种比较重的操作,但是如果不移动的话,会有内存碎片,内存分配时效率会变低,所以由于内存分配的频率会比垃圾回收的频率高很多,所以从吞吐量方面看,标记-整理法高于标记-清除法。 + +**延迟高** + +但是由于需要移动对象,停顿时间会比较长,垃圾回收时延迟会高一些,强调低延迟的CMS收集器一般是大部分时候用标记-清除算法,当内存碎片化程度达到一定程度时,触发Full GC,会使用标记-整理算法清理一次。 + +#### 分代收集算法 + +弱分代假说:就是绝大部分对象都是朝生夕灭。 + +强分代假说:熬过越多次垃圾收集的对象就越难以消忙。 + +就是现在的系统一般都比较复杂,堆中的对象也会比较多,如果使用对所有对象都分析是否需要回收,那么效率会比较低,所以有了分代收集算法,就是对熬过垃圾回收次数不同的对象进行分类,分为新生代和老年代,采用不同回收策略。 + +新生代存活率低,使用标记-复制算法。新生代发生的垃圾收集交Minor GC,发生频率较高 + +老年代存活率高,使用标记-清除算法,或者标记-整理算法。(CMS垃圾收集器一般是多数时间采用标记-清除算法,内存碎片化程度较高时,使用标记-整理算法收集一次)。老年代内存满时会触发Major GC(Full GC),一般触发的频率比较低。 + +#### 如何确定一个对象是否可以被回收? + +有两种算法,一种是引用计数法,可以记录每个对象被引用的数量来确定,当被引用的数量为0时,代表可以回收。 +一种是可达性分析法。就是判断对象的引用链是否可达来确定对象是否可以回收。就是把所有对象之间的引用关系看成是一个图,通过从一些GC Roots对象作为起点,根据这些对象的引用关系一直向下搜索,走过的路径就是引用链,当所有的GCRoots对象的引用链都到达不了这个对象,说明这个对象不可达,可以回收。 +GCRoots对象一般是当前肯定不会被回收的对象,一般是虚拟机栈中局部变量表中的对象,方法区的类静态变量引用的对象,方法区常量引用的对象,本地方法栈中Native方法引用的对象。 + +### 内存分配策略有哪些? + +#### 内存划分策略: + +![image-20200303152150129](../static/image-20200303152150129.png) + +Serial收集器中,新生代与老年代的内存分配是1:2,然后新生代分为Eden区,From区,To区,比例是8:1:1。 + +##### 新生代 + +分为Eden,From Survivor,To Survivor,8:1:1 + +Eden用来分配新对象,满了时会触发Minor GC。 + +From Survivor是上次Minor GC后存活的对象。 + +To Survivor是用于下次Minor GC时存放存活的对象。 + +##### 老年代 + +用于存放存活时间比较长的对象,大的对象,当容量满时会触发Major GC(Full GC) + +#### 内存分配策略: + +1) 对象优先在Eden分配 + +当Eden区没有足够空间进行分配时,虚拟机将发起一次MinorGC。现在的商业虚拟机一般都采用复制算法来回收新生代,将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。 当进行垃圾回收时,将Eden和Survivor中还存活的对象一次性地复制到另外一块空闲的Survivor空间上,最后处理掉Eden和刚才的Survivor空间。(HotSpot虚拟机默认Eden和Survivor的大小比例是8:1)当Survivor空间不够用时,需要依赖老年代进行分配担保。 + +2) 大对象直接进入老年代 + +所谓的大对象是指,需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组,为了避免大对象在Eden和两个Survivor区之间进行来回复制,所以当对象超过-XX:+PrintTenuringDistribution参数设置的大小时,直接从老年代分配 + +3) 长期存活的对象将进入老年代 + +当对象在新生代中经历过一定次数(XX:MaxTenuringThreshold参数设置的次数,默认为15)的Minor GC后,就会被晋升到老年代中。 + +4) 动态对象年龄判定 + +为了更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中某个年龄所有对象大小的总和>Survivor空间的50%,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。 + +### MinorGC和FullGC是什么? + +Minor GC:对新生代进行回收,不会影响到年老代。因为新生代的 Java 对象大多死亡频繁,所以 Minor GC 非常频繁,一般在这里使用速度快、效率高的算法,使垃圾回收能尽快完成。 + +Full GC:也叫 Major GC,对整个堆进行回收,包括新生代和老年代。由于Full GC需要对整个堆进行回收,所以比Minor GC要慢,因此应该尽可能减少Full GC的次数,导致Full GC的原因包括:老年代被写满和System.gc()被显式调用等。 + +### 触发Minor GC的条件有哪些? + +1.为新对象分配内存时,新生代的Eden区空间不足。 +新生代回收日志: + +```java +2020-05-12T16:15:10.736+0800: 7.803: [GC (Allocation Failure) 2020-05-12T16:15:10.736+0800: 7.803: [ParNew: 838912K->22016K(943744K), 0.0982676 secs] 838912K->22016K(1992320K), 0.0983280 secs] [Times: user=0.19 sys=0.01, real=0.10 secs] +``` +### 触发Full GC的条件有哪些? + +主要分为三种: +#### 1.system.gc() + +代码中调用system.gc()方法,建议JVM进行垃圾回收。 + +#### 2.方法区空间不足 + + 方法区中存放的是一些类的信息,当系统中要加载的类、反射的类和调用的方法较多时,方法区可能会被占满,触发 Full GC + +#### 3.老年代空间不足 + +而老年代空间不足又有很多种情况: +**3.1 Minor GC后,老年代存放不下晋升对象** +在进行 MinorGC 时, Survivor Space 放不下存活的对象,此时会让这些对象晋升,只能将它们放入老年代,而此时老年代也放不下时造成的。 +还有一些情况也会导致新生代对象晋升,例如存活对象经历的垃圾回收次数超过一定次数(XX:MaxTenuringThreshold参数设置的次数,默认为15),那么会导致晋升, +或者在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。 +**3.2 Concurrent Mode Failure** +在执行 CMS GC 的过程中,同时有对象要放入老年代,而此时老年代空间不足造成的。 +**3.3 历次晋升的对象平均大小>老年代的剩余空间** +这是一个较为复杂的触发情况, HotSpot为了避免由于新生代对象晋升到老年代导致老年代空间不足的现象, +在进行 Minor GC时,做了一个判断,如果之前统计所得到的 MinorGC 晋升到老年代的平均大小大于老年代的剩余空间,那么就直接触发 Full GC。 +**3.4 老年代空间不足以为大对象分配内存** +因为超过阀值(-XX:+PrintTenuringDistribution参数设置的大小时)的大对象,会直接分配到老年代,如果老年代空间不足,会触发Full GC。 + + +### 垃圾收集器有哪些? + +一般老年代使用的就是**标记-整理**,或者**标记-清除**+标记-整理结合(例如CMS) + +新生代就是**标记-复制**算法 + +| 垃圾收集器 | 特点 | 算法 | 适用内存区域 | +| ------------------------------------------- | ---------------------------------------------------- | ------------------- | -------------- | +| Serial | 单个GC线程进行垃圾回收,简单高效 | 标记-复制 | 新生代 | +| Serial Old | 单个GC线程进行垃圾回收 | 标记-整理 | 老年代 | +| ParNew | 是Serial的改进版,就是可以多个GC线程一起进行垃圾回收 | 标记-复制 | 新生代 | +| Parallel Scanvenge收集器(吞吐量优先收集器) | 高吞吐量,吞吐量=执行用户线程的时间/CPU执行总时间 | 标记-复制 | 新生代 | +| Parallel Old收集器 | 支持多线程收集 | 标记-整理 | 老年代 | +| CMS收集器(并发低停顿收集器) | 低停顿 | 标记-清除+标记-整理 | 老年代 | +| G1收集器 | 低停顿,高吞吐量 | 标记-复制算法 | 老年代,新生代 | + +#### Serial收集器(标记-复制算法) + +就是最简单的垃圾收集器,也是目前 JVM 在 Client 模式默认的垃圾收集器,在进行垃圾收集时会停止用户线程,然后使用一个收集线程进行垃圾收集。主要用于新生代,使用标记-复制算法。 + +优点是简单高效(与其他收集器的单线程比),内存占用小,因为垃圾回收时就暂停所有用户线程,然后使用一个单线程进行垃圾回收,不用进行线程切换。 + +缺点是收集时必须停止其他用户线程。 + +#### Serial Old收集器(标记-整理算法) + +跟Serial收集器一样,不过是应用于老年代,使用标记-整理算法。 + +![image-20200228184419205](../static/image-20200228184419205.png) + +#### ParNew收集器(标记-复制算法) + +ParNew收集器是Serial收集器的多线程并行版本,在进行垃圾收集时可以使用多个线程进行垃圾收集。 + +与Serial收集器主要区别就是支持多线程收集,ParNew收集器应用广泛(JDK9以前,服务端模式垃圾收集组合官方推荐的是ParNew+CMS),因为只有Serial和ParNew才能配合CMS收集器(应用于老年代的并发收集器)一起工作。 + +![image-20200228185412716](../static/image-20200228185412716.png) + +#### Parallel Scanvenge收集器(吞吐量优先收集器) + +也支持多线程收集,它的目标是达到一个可控制的吞吐量,就是运行用户代码的时间/(运行用户代码的时间+垃圾回收的时间)比值。高吞吐量可以最高效率地利用处理器资源,尽快完成程序运算任务,适合不需要太多的交互分析任务。不支持并发收集,进行垃圾回收时会暂停用户线程,使用多个垃圾回收线程进行垃圾回收。 + +#### Parallel Old收集器 + +是Parallel Scanvenge老年代版本,支持多线程收集,使用标记整理法实现的。 + +![image-20200228191619466](../static/image-20200228191619466.png) + +#### CMS 收集器(老年代并发低停顿收集器) + +CMS收集器是第一个支持并发收集的垃圾收集器,在垃圾收集时,用户线程可以和收集线程一起工作,它的执行目标是达到最短回收停顿时间,以获得更好的用户体验。 + +CMS英文是Concurrent Mark Sweep,是基于标记-清除法+标记-整理算法实现的,步骤如下: + +![image-20200228195544758](../static/image-20200228195544758.png) + +#### 1.初始标记 + +标记那些GC Roots可以直接关联到的对象。 + +#### 2.并发标记(可以与用户线程并发执行) + +从GC Roots能关联到的对象直接向下遍历整个对象图,耗时较长,但是可以与用户线程并发执行。 + +整个标记过程是采用三色标记法来实现的: + +**白色**:代表还没有遍历的对象。 + +**灰色**:代表已遍历到的对象,但是它直接引用的对象还没有遍历完。 + +**黑色**:代表已遍历到的对象,它直接引用的对象也都已经遍历完。 + +##### 执行流程: + +1.先将所有对象放到白色集合,然后将GC Roots能关联的对象添加到灰色集合。 + +2.对灰色集合中对象进行遍历,假设从灰色集合中取出的对象是A,将对象A引用的所有对象添加到灰色集合,全部添加完毕后,将A添加到黑色集合。 + +3.重复第2步,直到灰色集合为空。 + +##### 三色标记法的一些问题: + +##### 1.对于多标的情况,会生成浮动垃圾 + +![img](../static/7779607-7a5ce353116237e2.png) +对于这种多标的情况,对象E/F/G是“应该”被回收的。然而因为E已经变为灰色了,其仍会被当作存活对象继续遍历下去。最终的结果是:这部分对象仍会被标记为存活,即本轮GC不会回收这部分内存。 +这部分本应该回收 但是 没有回收到的内存,被称之为“浮动垃圾”。浮动垃圾并不会影响垃圾回收的正确性,只是需要等到下一轮垃圾回收中才被清除。 +另外,针对并发标记开始后的新对象,通常的做法是直接全部当成黑色,本轮不会进行清除。这部分对象期间可能会变为垃圾,这也算是浮动垃圾的一部分。 + +##### 2.对于引用变动后导致漏标情况的处理 + + ![](../static/7779607-dab8f35ecb417433.png) +##### 增量更新法 + +对于这种漏标的情况,CMS垃圾收集器使用的是增量更新法,就是将引用变化后的引用情况进行记录,然后之后进行标记。也就是当E->G变成了E->null,D->G,会对更改后的引用D->G进行记录,用于在重新标记阶段对这种情况进行处理。 + +##### 原始快照法 + +就是对于这种E->G,然后改成D->G,正常来说,应该可能会漏掉,因为D已经是黑色对象了,就不会遍历G了,G1垃圾收集器对这种情况的处理是保存原始快照,就是在并发标记过程中,引用的变动,都会对变动前的引用情况进行记录,会按照变动前的引用情况进行标记,也就是即便E->G变成了E->null,D->G变化了,还是会记录E->G的引用情况,用于在重新标记阶段对这种情况进行处理。 +#### 3.重新标记 + +由于标记时,用户线程在运行,并发标记阶段存在一些标记变动的情况,这一阶段就是修正这些记录。(CMS采用增量更新算法来解决,主要是将更改后的引用关系记录,之后增量更新) + +#### 4.并发清除 + +清除标记阶段判断的已经死亡的对象,由于不需要移动存活对象,这个阶段也可以与用户线程并发执行。 + +#### G1收集器(Garbage First收集器,标记-整理算法,老年代,新生代都进行回收) + +目标是在延迟可控(用户设定的延迟时间)的情况下获得尽可能高的吞吐量。 + +JDK9以前,服务端模式默认的收集器是Parallel Scavenge+Serial Old。JDK9及之后,默认收集器是G1。G1不按照新生代,老年代进行划分,而是将Java堆划分为多个大小相等的独立Region,每一个Region可以根据需要,扮演新生代的Eden空间,Survivor空间,老年代Old空间和用于分配大对象的Humongous区。回收思路是G1持续跟踪各个Region的回收价值(回收可释放的空间和回收所需时间),然后维护一个优先级列表,在用户设定的最大收集停顿时间内,优先回收那些价值大的Region。 + +JDK 8 和9中,Region的大小是通过(最大堆大小+最小堆大小)的平均值/2048,一般是需要在1到32M之间。G1认为2048是比较理想的Region数量 + +![img](../static/640.jpeg) + +**G1对象分配策略** + +说起对象的分配,我们不得不谈谈对象的分配策略。它分为4个阶段: + +1. 栈上分配 +2. TLAB(Thread Local Allocation Buffer)线程本地分配缓冲区 +3. 共享Eden区中分配 +4. Humongous区分配(超过Region大小50%的对象) + +对象在分配之前会做逃逸分析,如果该对象只会被本线程使用,那么就将该对象在栈上分配。这样对象可以在函数调用后销毁,减轻堆的压力,避免不必要的gc。 如果对象在栈是上分配不成功,就会使用TLAB来分配。TLAB为线程本地分配缓冲区,它的目的为了使对象尽可能快的分配出来。如果对象在一个共享的空间中分配,我们需要采用一些同步机制来管理这些空间内的空闲空间指针。在Eden空间中,每一个线程都有一个固定的分区用于分配对象,即一个TLAB。分配对象时,线程之间不再需要进行任何的同步。 + +对TLAB空间中无法分配的对象,JVM会尝试在共享Eden空间中进行分配。如果是大对象,则直接在Humongous区分配。 + +最后,G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW)的。下面我们将分别介绍一下这2种模式。 + +**G1 Young GC** + +Young GC主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。在这种情况下,Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。最终Eden空间的数据为空,GC停止工作,应用线程继续执行。 + +![img](../static/640-20200510171526372.jpeg) + +##### Region如何解决跨代指针? + +因为老年代old区也会存在对新生代Eden区的引用,如果只是为了收集Eden区而对整个老年代进行扫描,那样开销太大了,所以G1其实会将每个Region分为很多个区,每个区有一个下标,当这个区有对象被其他Region引用时,那么CardTable对应下标下值为0,然后使用一个**Rset来存储老年代Region对当前这个新生代Region的引用**,Key是别的Region的起始地址,Value是一个集合,里面的元素是Card Table的Index。对跨代引用的扫描,只需要扫描RSet就行了。 + +在CMS中,也有RSet的概念,在老年代中有一块区域用来记录指向新生代的引用。这是一种point-out,在进行Young GC时,扫描根时,仅仅需要扫描这一块区域,而不需要扫描整个老年代。 + +但在G1中,并没有使用point-out(就是记录当前Region对其他Region中对象的引用),这是由于一个Region太小,Region数量太多,如果是用point-out的话,如果需要计算一个Region的可回收的对象数量,需要把所有Region都是扫描一遍会造成大量的扫描浪费,有些根本不需要GC的分区引用也扫描了。于是G1中使用point-in来解决。point-in的意思是哪些Region引用了当前Region中的对象。这样只需要将当前Region中这些对象当做初始标记时的根对象来扫描就可以扫描出因为有跨代引用需要存活的对象,避免了无效的扫描。 + +##### 由于新生代有多个,那么我们需要在新生代之间记录引用吗? + +这是不必要的,原因在于每次GC时,所有新生代都会被扫描,所以只需要记录**老年代的Region对新生代的这个Region之间的引用**即可。 + +需要注意的是,如果引用的对象很多,赋值器需要对每个引用做处理,赋值器开销会很大,为了解决赋值器开销这个问题,在G1 中又引入了另外一个概念,卡表(Card Table)。一个Card Table将一个分区在逻辑上划分为固定大小的连续区域,每个区域称之为卡。卡通常较小,介于128到512字节之间。CardTable通常为字节数组,由Card的索引(即数组下标)来标识每个分区的空间地址。默认情况下,每个卡都未引用。当一个地址空间有引用时,这个地址空间对应的数组索引的值被标记为"0",即标记为脏引用,此外RSet也将这个数组下标记录下来。一般情况下,这个RSet其实是一个Hash Table集合(每个线程对应一个Hash Table,主要是为了减少多线程并发更新RSet的竞争),每个哈希表的Key是别的Region的起始地址,Value是一个集合,里面的元素是Card Table的Index。 + +如果Rset是记录每个外来Region对当前Region中对象的引用,这样数量就太多了,所以Card Table只是有很多Byte字节,每个字节记录了Region对应的一个内存区域(卡页)是否是dirty的,为1代表dirty,也就是有其他Region对这个卡页中的对象进行引用。 + +![img](../static/2579123-e0b8898d895aee05.png) + +**G1 MixGC** + +MixGC不仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的老年代分区。Young GC回收是把新生代活着的对象都拷贝到Survivor的特定区域(Survivor to),剩下的Eden和Survivor from就可以全部回收清理了。那么,mixed GC就是把一部分老年区的region加到Eden和Survivor from的后面,合起来称为collection set, 就是将被回收的集合,下次mixed GC evacuation把他们所有都一并清理。选old region的顺序是垃圾多的(存活对象少)优先。 + +它的GC步骤分2步: + +1. 全局并发标记(global concurrent marking) +2. 拷贝存活对象(evacuation) + +在进行Mix GC之前,会先进行global concurrent marking(全局并发标记)。 global concurrent marking的执行过程是怎样的呢? + +在G1 GC中,它主要是为Mixed GC提供标记服务的,并不是一次GC过程的一个必须环节。global concurrent marking的执行过程分为五个步骤: + +- 初始标记(initial mark,STW) + 在此阶段,G1 GC 对根进行标记。该阶段与常规的(STW) 新生代垃圾回收密切相关。 +- 根区域扫描(root region scan) + G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 新生代垃圾回收。 +- 并发标记(Concurrent Marking) + G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 新生代垃圾回收中断 +- 最终标记(Remark,STW) + 该阶段是 STW 回收,帮助完成标记周期。G1 GC清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。 +- 清除垃圾(Cleanup,STW) + 在这个最后阶段,G1 GC 执行统计和 RSet 净化的 STW 操作。在统计期间,G1 GC 会识别完全空闲的区域和可供进行混合垃圾回收的区域。清理阶段在将空白区域重置并返回到空闲列表时为部分并发。 + +##### 收集步骤: + +![image-20200302181639409](../static/image-20200302181639409.png) + +**初始标记** 只标记GC Roots直接引用的对象 + +**并发标记** 从GC Roots开始对堆中对象进行可达性分析,扫描整个对象图,找出要回收的对象。(可以与用户线程并发执行) + +**最终标记** 对并发标记阶段,由于用户线程执行造成的改动进行修正,使用原始快照方法。 + +**筛选回收** 对Region进行排序,根据回收价值,选择任意多个Region构成回收集,将存活对象复制到空的Region中去,因为涉及到存活对象的移动,所以是暂停用户线程的。 + +相关资料: + +https://www.jianshu.com/p/aef0f4765098 + +### 三色标记法,引用计数, Mark-Sweep法(标记清除法)三者的区别? + +三色标记法可以与用户线程并发执行。 + +引用计数法主要没有办法解决循环引用的问题。 + +标记清除法主要流程就是一开始认为所有对象是0,然后从GC Roots对象开始向下遍历,**递归地**遍历所有对象,将能遍历到的对象标记为1。然后遍历结束后,标记为0的对象被认为是可以被销毁的。主要问题在于标记过程需要Stop the world,也就是需要停止用户线程,只有标记线程能运行。 + +扩展阅读: + +https://zhuanlan.zhihu.com/p/87790329 + +### 垃圾收集器相关的参数 + +-XX:+UseSerialGC,虚拟机运行在Client 模式下的默认值,打开此开关后,使用Serial + Serial Old 的收集器组合进行内存回收 + +-XX:+UseConcMarkSweepGC,打开此开关后,使用**ParNew + CMS + Serial Old** 的收集器组合进行内存回收。Serial Old 收集器将作为CMS 收集器出现Concurrent Mode Failure失败后的后备收集器使用。(我们的线上服务用的都是这个) + +-XX:+UseParallelGC,虚拟机运行在Server 模式下的默认值,打开此开关后,使用**Parallel Scavenge + Serial Old**(PS MarkSweep)的收集器组合进行内存回收。 + +-XX:+UseParallelOldGC,打开此开关后,使用Parallel Scavenge + Parallel Old 的收集器组合进行内存回收。 + +-XX:+UseG1GC,打开此开关后,采用 Garbage First (G1) 收集器 + +-XX:+UseParNewGC,在JDK1.8被废弃,在JDK1.7还可以使用。打开此开关后,使用ParNew + Serial Old 的收集器组合进行内存回收 + +参考链接: + +https://www.pianshen.com/article/7390335728/ + +### 目前通常使用的是什么垃圾收集器? + +##### 怎么查询当前JVM使用的垃圾收集器? + +使用这个命令可以查询当前使用的垃圾收集器 +`java -XX:+PrintCommandLineFlags -version` + +另外这个命令可以查询到更加详细的信息 + +`java -XX:+PrintFlagsFinal -version | grep GC` + +我们在IDEA中启动的一个Springboot的项目,默认使用的垃圾收集器参数是 +-XX:+UseParallelGC + +``` +-XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC +java version "1.8.0_73" +Java(TM) SE Runtime Environment (build 1.8.0_73-b02) +Java HotSpot(TM) 64-Bit Server VM (build 25.73-b02, mixed mode) +``` +##### Parallel Scavenge+Serial Old + +JDK8默认情况下服务端模式下JVM垃圾回收参数是-XX:+UseParallelGC参数,也就是会使用**Parallel Scavenge+Serial Old**的收集器组合,进行内存回收。 + +##### ParNew+CMS + +但是一般如果我们的后端应用不是那种需要进行大量计算的应用,基于低延迟的考虑,可以考虑使用-XX:+UseConcMarkSweepGC进行垃圾收集,这种配置下会使用ParNew来收集新生代内存,CMS垃圾回收器收集老年代内存。 + +##### G1 + +在JDK9时,默认的垃圾收集器是G1收集器,也可以使用-XX:+UseG1GC参数来启动G1垃圾收集器。 + + + +![img](../static/519126-20180623154635076-953076776.png) + +### 容器的内存和 jvm 的内存有什么关系?参数怎么配置? + +一般在使用容器部署Java应用时,一般为了充分利用物理机的资源,会在物理机上部署多个容器应用,然后对每个容器设置最大内存的限制,但是JVM的最大堆默认值一般取得的物理机最大内存的25%,一旦应用内存超出容器的最大内存限制,容器就会把应用进程kill掉,然后重启。为了解决这个问题,有3种解决方案: + +1.在应用的JVM参数中添加-Xmx 最大堆内存的大小,可以设置为容器最大内存限制的75%。一旦你在修改了容器的最大内存限制,每个应用的JVM参数-Xmx 也许需要同步进行修改。 + +2.就是添加这几个参数可以让Java应用感知容器的内存限制,从而在设置最大堆内存大小时,根据容器的内存限制进行设置。 +`-XX:+UnlockExperimentalVMOptions +-XX:+UseCGroupMemoryLimitForHeap +-XX:MaxRAMFraction=2` + +下面是MaxRAMFraction取不同的值时,最大堆内存与容器最大内存限制的比例。考虑到除了内存中除了最大堆内存以外,还有方法区,线程栈等需要需要占用内存,所以MaxRAMFraction一般至少取2会比较合适。如果取值为1,在最大堆内存占满时,可能Java应用占用的总内存会超过容器最大内存限制。 + + +![image-20210207145307777](../static/image-20210207145307777.png) + +3.在JDK8以后,JVM增加了容器感知功能,就是如果不显示指定-Xmx2048m 最大堆内存大小, -Xms2048m最小堆内存大小,会取容器所在的物理机的内存的25%作为最大堆内存大小,也可以通过这几个参数来设置堆内存占容器内存的比例 +-XX:MinRAMPercentage 最小堆内存大小占容器内存限制的比例 +-XX:MaxRAMPercentage 最大堆内存大小占容器内存限制的比例 +-XX:InitialRAMPercentage 初始堆内存大小占容器内存限制的比例 + +### 如何大体估算java进程使用的内存呢? + +Max memory = [-Xmx] + [-XX:MaxPermSize] + number_of_threads * [-Xss] + +-Xss128k: 设置每个线程的堆栈大小.JDK5.0以后默认每个线程的栈大小为1M。 + +-Xms 堆内存的初始大小,默认为物理内存的1/64 +-Xmx 堆内存的最大大小,默认为物理内存的1/4 +-Xmn 堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn + +-Xss 设置每个线程可使用的内存大小,即栈的大小。在相同物理内存下,减小这个值能生成更多的线程,当然操作系统对一个进程内的线程数还是有限制的,不能无限生成。线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。 + +### 怎么获取 dump 文件?怎么分析? + +1.启动时配置,出现OOM问题时自动生成 +JVM启动时增加两个参数: + + +```java +//出现 OOME 时生成堆 dump: +-XX:+HeapDumpOnOutOfMemoryError +//生成堆文件地址: +-XX:HeapDumpPath=/home/liuke/jvmlogs/ +``` + +2.执行jmap命令立即生成 +发现程序异常前通过执行指令,直接生成当前JVM的dmp文件,6214是指JVM的进程号 + +```java +jmap -dump:format=b,file=/home/admin/logs/heap.hprof 6214 +``` + +获得heap.hprof以后,执行`jvisualvm`命令打开使用Java自带的工具Java VisualVM来打开heap.hprof文件,就可以分析你的Java线程里面对象占用堆内存的情况了 + +由于第一种方式是一种事后方式,需要等待当前JVM出现问题后才能生成dmp文件,实时性不高,第二种方式在执行时,JVM是暂停服务的,所以对**线上的运行会产生影响**。所以建议第一种方式。 + +### gc日志怎么看? + +可以在启动Java应用的命令中添加这些参数,指定生成垃圾收集的日志的路径,可以记录垃圾收集的情况。 + +`-XX:+PrintGCDetails +-XX:+PrintGCDateStamps +-Xloggc:/var/log/gc.log +` + +这是一条Minor GC的回收日志(一般GC (Allocation Failure)代表Minor GC,老年代垃圾回收一般打印的是Full GC (Metadata GC Threshold) ) + +```java +2020-05-07T16:28:02.845+0800: 78210.469: [GC (Allocation Failure) 2020-05-07T16:28:02.845+0800: 78210.469: [ParNew: 68553K->466K(76672K), 0.0221963 secs] 131148K->63062K(2088640K), 0.0223082 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] +``` + +**GC (Allocation Failure)** + +代表Eden区分配内存失败触发Minor GC。 +**2020-05-07T16:28:02.845+0800: 78210.469** + +是发生的时间。 +**68553K->466K(76672K)** + +代表垃圾回收前新生代使用内存是68MB,剩余0.4MB,总内存是76MB。 +**0.0221963 secs** + +是垃圾回收耗时。 +**131148K->63062K(2088640K)** + +代表堆区回收前使用131MB,63MB,总内存是2088MB。 + +**[Times: user=0.02 sys=0.00, real=0.02 secs]** + +用户态耗时0.02s,内核态耗时0s,总耗时0.02s + + PS:有一个网站,可以对上传GC.log的日志进行分析,解析日志文件,统计出垃圾收集总占用的时间,以及新生代,老年代的内存使用峰值,https://gceasy.io/ + +![image-20210208162512025](../static/image-20210208162512025.png) + +### cpu 使用率特别高,怎么排查?通用方法?定位代码?cpu高的原因? + +[CPU飙高,频繁GC,怎么排查?](https://mp.weixin.qq.com/s?src=11×tamp=1589109647&ver=2330&signature=LegOQuRkl7V9kFT6JT3Kg-9X4VYN40OmTyVRlSOFLppbFy*PTUWPAb2iyCFqz-ka9RRCje4fXGxS3sqw1z3JYQ3MRxzuI-UTLtlx-fV8VA8CLDWOFFfIuIVwrAeUdDHb&new=1) + +[jstack命令:教你如何排查多线程问题](https://mp.weixin.qq.com/s?__biz=MzI3ODcxMzQzMw==&mid=2247484624&idx=1&sn=a907100b51aca8bd651aebdb9aca532f&chksm=eb5381e6dc2408f092159c453a452c2781d374ca43f0984dde87688de8a1f8d9196dfd747124&scene=21#wechat_redirect) +``` + jstat -gcutil 29530 1000 10 + 垃圾回收信息统计,29530是pid,1000是每1秒打印一次最新信息,10是最多打印10次 +``` + + +### 怎么排查CPU占用率过高的问题? +1.首先使用`top`命令查看CPU占用率高的进程的pid。 +``` +top - 15:10:32 up 523 days, 3:47, 1 user, load average: 0.00, 0.01, 0.05 +Tasks: 95 total, 1 running, 94 sleeping, 0 stopped, 0 zombie +%Cpu(s): 1.7 us, 0.5 sy, 0.0 ni, 95.7 id, 2.2 wa, 0.0 hi, 0.0 si, 0.0 st +KiB Mem : 16267904 total, 6940648 free, 2025316 used, 7301940 buff/cache +KiB Swap: 16777212 total, 16776604 free, 608 used. 13312484 avail Mem + + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND +14103 hadoop 20 0 2832812 203724 18392 S 3.7 1.3 977:08.04 java +14010 hadoop 20 0 2897344 285392 18660 S 0.3 1.8 513:30.49 java +14284 hadoop 20 0 3052556 340436 18636 S 0.3 2.1 1584:47 java +14393 hadoop 20 0 2912460 504112 18632 S 0.3 3.1 506:43.68 java + 1 root 20 0 190676 3404 2084 S 0.0 0.0 4:31.47 systemd + 2 root 20 0 0 0 0 S 0.0 0.0 0:04.77 kthreadd + 3 root 20 0 0 0 0 S 0.0 0.0 0:10.16 ksoftirqd/0 +``` +2.使用`top -Hp 进程id`获得该进程下各个线程的CPU占用情况,找到占用率最高的线程的pid2, +使用`printf "%x\n" pid2`命令将pid2转换为16进制的数number。 + +``` +top - 15:11:01 up 523 days, 3:48, 1 user, load average: 0.00, 0.01, 0.05 +Threads: 69 total, 0 running, 69 sleeping, 0 stopped, 0 zombie +%Cpu(s): 12.8 us, 0.1 sy, 0.0 ni, 87.0 id, 0.1 wa, 0.0 hi, 0.0 si, 0.0 st +KiB Mem : 16267904 total, 6941352 free, 2024612 used, 7301940 buff/cache +KiB Swap: 16777212 total, 16776604 free, 608 used. 13313188 avail Mem + + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND +14393 hadoop 20 0 2912460 504112 18632 S 0.0 3.1 0:00.01 java +14411 hadoop 20 0 2912460 504112 18632 S 0.0 3.1 0:01.95 java +14412 hadoop 20 0 2912460 504112 18632 S 0.0 3.1 0:16.18 java +14413 hadoop 20 0 2912460 504112 18632 S 0.0 3.1 0:12.79 java +14414 hadoop 20 0 2912460 504112 18632 S 0.0 3.1 8:09.10 java +``` +3.使用`jstack pid`获得进程下各线程的堆栈信息,nid=0xnumber的线程即为占用率高的线程,查看它是在执行什么操作。(`jstack 5521 | grep -20 0x1596`可以获得堆栈信息中,会打印匹配到0x1596的上下20行的信息。) + +例如这个线程是在执行垃圾回收: +``` +"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f338c01f000 nid=0x1593 runnable +``` + +##### JVM相关的异常 + +#### 1.stackoverflow + +这种就是栈的空间不足,就会抛出这个异常,一般是递归执行一个方法时,执行方法深度太深时出现。Java执行一个方法时,会创建一个栈帧来存放局部变量表,操作数栈,如果分配栈帧时,栈空间不足,那么就会抛出这个异常。 + +(栈空间可以设置-Xss参数实现,默认为1M,如果参数) + +### JVM调优有哪些工具? + +#### jstat + +jstat可以打印出当前JVM运行的各种状态信息,例如新生代内存使用情况,老年代内存使用情况,以及垃圾回收的时间。Minor GC发生总次数,总耗时,Full GC发生总次数,总耗时。(jmap -heap命令也可以打印出堆中各个分区的内存使用情况,但是不能定时监测,持续打印。例如每1s打印当前的堆中各个分区的内存使用情况,一直打印100次。) +``` +//5828是java进程id,1000是打印间隔,每1000毫秒打印一次,100是总共打印100次 +jstat -gc 5828 1000 100 +``` + +打印结果如下: + +![image-20200725204237083](../static/image-20200725204237083.png) + +各个参数的含义如下: + +`S0C` 新生代中第一个survivor(幸存区)的总容量 (字节) + +`S1C `新生代中第二个survivor(幸存区)的总容量 (字节) + +`S0U` 新生代中第一个survivor(幸存区)目前已使用空间 (字节) + +`S1U` 新生代中第二个survivor(幸存区)目前已使用空间 (字节) + +`EC` 新生代中Eden区的总容量 (字节) + +`EU` 新生代中Eden区目前已使用空间 (字节) + +`OC` 老年代的总容量 (字节) + +`OU` 老年代代目前已使用空间 (字节) + +`YGC` 目前新生代垃圾回收总次数 + +`YGCT` 目前新生代垃圾回收总消耗时间 + +`FGC` 目前full gc次数总次数 + +`FGCT` 目前full gc次数总耗时,单位是秒 + +`GCT` 垃圾回收总耗时 + +一般还可以使用`jstat -gcutil `:统计gc信息,这样打印出来的结果是百分比,而不是实际使用的空间,例如jstat -gcutil 1 1000 100 + +例如,S0代表 新生代中第一个survivor区的空间使用了73.19%,E代表新生代Eden区使用了51%,O代表老年代食堂了98% + +![image-20200725204859356](../static/image-20200725204859356.png) + +| 参数 | 描述 | +| ---- | -------------------------------------------------------- | +| S0 | 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比 | +| s1 | 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比 | +| E | 年轻代中Eden已使用的占当前容量百分比 | +| O | old代已使用的占当前容量百分比 | +| M | 元空间(MetaspaceSize)已使用的占当前容量百分比 | +| CCS | 压缩使用比例 | +| YGC | 年轻代垃圾回收次数 | +| FGC | 老年代垃圾回收次数 | +| FGCT | 老年代垃圾回收消耗时间 | +| GCT | 垃圾回收消耗总时间 | + +![image-20200731152050340](../static/image-20200731152050340.png) + +#### jstack + +jstack可以生成当前JVM的线程快照,也就是当前每个线程当前的状态及正在执行的方法,锁相关的信息。`jstack -l 进程id `,-l代表除了堆栈信息外,还会打印锁的附加信息。jstack还会检测出死锁信息。一般可以用于定位线程长时间停顿,线程间死锁等问题。 + +例如在下面的例子中,第一个线程获取到lock1,再去获取lock2,第二个线程先获取到lock2,然后再去获取lock1。每个线程都只获得了一个锁,同时在获取另外一个锁,就会进入死锁状态。 + +```java +public static void main(String[] args) { + final Integer lock1 = new Integer(1); + final String lock2 = new String(); + ExecutorService executorService = Executors.newCachedThreadPool(); + executorService.execute(new Runnable() { + @Override + public void run() { + synchronized (lock1) { + System.out.println("线程1获得了lock1"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("线程1休眠结束"); + System.out.println("线程1开始尝试获取lock2"); + synchronized (lock2) { + System.out.println("线程1获得了lock2"); + } + } + } + }); + executorService.execute(new Runnable() { + @Override + public void run() { + synchronized (lock2) { + System.out.println("线程2获得了lock2"); + System.out.println("线程2开始尝试获取lock1"); + synchronized (lock1) { + System.out.println("线程2获得了lock2"); + } + } + } + }); + } +``` + +使用`jstack -l 进程id`就可以打印出当前的线程信息 + +![image-20200726165954263](../static/image-20200726165954263.png) + +以及各个线程的状态,执行的方法(pool-1-thread-1和pool-1-thread-2分别代表线程池的第一个线程和第二个线程): + +![image-20200726170056096](../static/image-20200726170056096.png) + +#### jmap + +##### jmap -heap + +这个命令可以生成当前堆栈快照。使用 `jmap -heap 进程id`可以打印出当前堆各分区内存使用情况的情况,新生代(Eden区,To Survivor区,From Survivor区),老年代区的内存使用情况。 + +使用jmap -heap查看内存使用情况的案例 + +![image-20200726173723112](../static/image-20200726173723112.png) + +##### jmap -histo + + **jmap -histo 进程id** 打印出当前堆中的对象统计信息,包括类名,每个类的实例数量,总占用内存大小。 + +```java +instances列:表示当前类有多少个实例。 +bytes列:说明当前类的实例总共占用了多少个字节 +class name列:表示的就是当前类的名称,class name 对于基本数据类型,使用的是缩写。解读:B代表byte ,C代表char ,D代表double, F代表float,I代表int,J代表long,Z代表boolean +前边有[代表数组,[I 就相当于int[] +对象数组用`[L+类名`表示 +``` + +![image-20200726174009407](../static/image-20200726174009407.png) + + + +##### jmap -dump + +使用`jmap -dump:format=b,file=dump.hprof 进程id`可以生成当前的堆栈快照,堆快照和对象统计信息,对生成的堆快照进行分析,可以分析堆中对象所占用内存的情况,检查大对象等。执行`jvisualvm`命令打开使用Java自带的工具**Java VisualVM**来打开堆栈快照文件,进行分析。可以用于排查内存溢出,内存泄露问题。在**Java VisualVM**里面可以看到每个类的实例对象占用的内存大小,以及持有这个对象的实例所在的类等等信息。 + +也可以配置启动时的JVM参数,让发送内存溢出时,自动生成堆栈快照文件。 + +```java +//出现 OOM 时生成堆 dump: +-XX:+HeapDumpOnOutOfMemoryError +//生成堆文件地址: +-XX:HeapDumpPath=/home/liuke/jvmlogs/ +``` + +使用**jmap -dump:format=b,file=/存放路径/heapdump.hprof 进程id**就可以得到堆转储文件,然后执行jvisualvm命令就可以打开JDK自带的jvisualvm软件。 + +例如在这个例子中会造成OOM问题,通过生成heapdump.hprof文件,可以使用jvisualvm查看造成OOM问题的具体代码位置。 + +```java +public class Test018 { + + ArrayList arrayList = new ArrayList(); + + public static void main(String[] args) { + Test018 test018 =new Test018(); + Random random = new Random(); + for (int i = 0; i < 10000000; i++) { + TestObject testObject = new TestObject(); + test018.arrayList.add(testObject); + } + } + private static class TestObject { + public byte[] placeholder = new byte[64 * 1024];//每个变量是64k + } +} + +-Xms20m -Xmx20m -verbose:gc -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/存放路径/heapdump.hprof +``` + +造成OOM问题的代码位置: + +![image-20200726190137193](../static/image-20200726190137193.png) + +堆内对象列表 + +![image-20200726190451781](../static/image-20200726190451781.png) + +占用内存最多的实例对象就是这个placeholder对象 + +![image-20200726190549717](../static/image-20200726190549717.png) + +#### MAT + +MAT主要可以用于分析内存泄露,可以查询dump堆转储文件中的对象列表,以及潜在的内存泄露的对象。 + +通过导入hprof文件,主页会展示潜在的内存泄露问题,比如下面这个例子中 + +```java +public class Test018 { + static ArrayList arrayList = new ArrayList(); + public static void main(String[] args) { + Random random = new Random(); + for (int i = 0; i < 10000000; i++) { + TestObject testObject = new TestObject(); + Test018.arrayList.add(testObject); + } + } + private static class TestObject { + public byte[] placeholder = new byte[64 * 1024]; + } +} +``` + +在详情页面Shortest Paths To the Accumulation Point表示GC root对象到内存消耗聚集点的最短路径,内存聚集点的意思就是占用了大量内存的对象,也就是可能发生; 内存泄露的对象。 + +![image-20200726205127282](../static/image-20200726205127282.png) + + + +然后在主页点击Histogram,进入Histogram页面可以看到对象列表,with incomming references 也就是可以查看所有对这个对象的引用(思路一般优先看占用内存最大对象;其次看数量最多的对象。)。我们这个例子中主要是byte[]数组分配了占用了大量的内存空间,而byte[]主要来自于Test018类的静态变量arrayList的每个TestObject类型的元素的placeholder属性。 + +![image-20200726205515840](../static/image-20200726205515840.png) + +![image-20200726205837727](../static/image-20200726205837727.png) + +同时可以点击 内存快照对比 功能对两个dump文件进行对比,判断两个dump文件生成间隔期间,各个对象的数量变化,以此来判断内存泄露问题。 + +![img](../static/640-20200726210041919.jpeg) + +![img](../static/640-20200726210058837.jpeg) + +### happens-before指的是什么? +happens-before主要是为保证Java多线程操作时的有序性和可见性,防止了编译器重排序对程序结果的影响。 +因为当一个变量出现一写多读,多读多写的情况时,就是多个线程读这个变量,然后至少一个线程写这个变量,因为编译器在编译时会对指令进行重排序,如果没有happens-before原则对重排序进行一些约束,就有可能造成程序执行不正确的情况。 + +具体有8条规则: +1.**程序次序性规则**:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。一段代码在单线程中执行的结果是有序的。(只在单线程有效,一写多读时,写的变量如果是没有使用volatile修饰时,是没法保证其他线程读到的变量是最新的值) + +2.**锁定规则**:一个锁的解锁操作总是要在加锁操作之前。 + +3.**volatile规则**:如果一个写操作去写一个volatile变量,一个读操作去读volatile变量,那么写操作一定是在读操作之前。 + +4.**传递规则**:操作A happens before 操作B, B happens before C,那么A一定是happens before C的。 + +5.**线程启动规则**:线程A执行过程中修一些共享变量,然后再调用threadB.start()方法来启动线程B,那么A对那些变量的修改对线程B一定是可见的。 + +6.**线程终结规则**:线程A执行过程中调用了threadB.join()方法来等待线程B执行完毕,那么线程B在执行过程中对共享变量的修改,在join()方法返回后,对线程A可见。 + +7.**线程中断规则**:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。 + +8.**对象终结规则**:一个对象的初始化完成先行发生于他的finalize()方法的开始; + +https://www.cnblogs.com/chenssy/p/6393321.html + +http://ifeve.com/java-%e4%bd%bf%e7%94%a8-happen-before-%e8%a7%84%e5%88%99%e5%ae%9e%e7%8e%b0%e5%85%b1%e4%ba%ab%e5%8f%98%e9%87%8f%e7%9a%84%e5%90%8c%e6%ad%a5%e6%93%8d%e4%bd%9c/#more-38824 \ No newline at end of file diff --git a/docs/JavaMultiThread.md b/docs/JavaMultiThread.md new file mode 100644 index 0000000..ba3743f --- /dev/null +++ b/docs/JavaMultiThread.md @@ -0,0 +1,1840 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) +# 多线程专题 + +#### [1.进程与线程的区别是什么?](#进程与线程的区别是什么?) +#### [2.进程间如何通信?](#进程间如何通信?) + +#### [3.Java中单例有哪些写法?](#Java中单例有哪些写法?) +#### [4.Java中创建线程有哪些方式?](#Java中创建线程有哪些方式?) +#### [5.如何解决序列化时可以创建出单例对象的问题?](#如何解决序列化时可以创建出单例对象的问题?) +#### [6.volatile关键字有什么用?怎么理解可见性,一般什么场景去用可见性?](#volatile关键字有什么用?怎么理解可见性,一般什么场景去用可见性?) +#### [7.Java中线程的状态是怎么样的?](#Java中线程的状态是怎么样的?) +#### [8.wait(),join(),sleep()方法有什么作用?](#wait(),join(),sleep()方法有什么作用?) +#### [9.Thread.sleep(),Object.wait(),LockSupport.park()有什么区别?](#Thread.sleep(),Object.wait(),LockSupport.park()有什么区别?) +#### [10.谈一谈你对线程中断的理解?](#谈一谈你对线程中断的理解?) +#### [11.线程间怎么通信?](#线程间怎么通信?) +#### [12.怎么实现实现一个生产者消费者?](#怎么实现实现一个生产者消费者?) +#### [13.谈一谈你对线程池的理解?](#谈一谈你对线程池的理解?) +#### [14.线程池有哪些状态?](#线程池有哪些状态?) + +### 进程与线程的区别是什么? + +#### 批处理操作系统 + +**批处理操作系统**就是把一系列需要操作的指令写下来,形成一个清单,一次性交给计算机。用户将多个需要执行的程序写在磁带上,然后交由计算机去读取并逐个执行这些程序,并将输出结果写在另一个磁带上。 + +批处理操作系统在一定程度上提高了计算机的效率,但是由于**批处理操作系统的指令运行方式仍然是串行的,内存中始终只有一个程序在运行**,后面的程序需要等待前面的程序执行完成后才能开始执行,而前面的程序有时会由于I/O操作、网络等原因阻塞,导致CPU闲置所以**批处理操作效率也不高**。 + +#### 进程的提出 + +批处理操作系统的瓶颈在于内存中只存在一个程序,进程的提出,可以让内存中存在多个程序,每个程序对应一个进程,进程是操作系统资源分配的最小单位。CPU采用时间片轮转的方式运行进程:CPU为每个进程分配一个时间段,称作它的时间片。如果在时间片结束时进程还在运行,则暂停这个进程的运行,并且CPU分配给另一个进程(这个过程叫做上下文切换)。如果进程在时间片结束前阻塞或结束,则CPU立即进行切换,不用等待时间片用完。多进程的好处在于一个在进行IO操作时可以让出CPU时间片,让CPU执行其他进程的任务。 + +#### 线程的提出 + +随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。于是就发明了线程,线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。 + +#### 进程和线程的区别 + +进程是计算机中已运行程序的实体,进程是操作系统资源分配的最小单位。而线程是在进程中执行的一个任务,是CPU调度和执行的最小单位。他们两个本质的区别是是否**单独占有内存地址空间及其它系统资源(比如I/O)**: + +* 进程单独占有一定的内存地址空间,所以进程间存在内存隔离,数据是分开的,数据共享复杂但是同步简单,各个进程之间互不干扰;而线程共享所属进程占有的内存地址空间和资源,数据共享简单,但是同步复杂。 + +* 进程单独占有一定的内存地址空间,一个进程出现问题不会影响其他进程,不影响主程序的稳定性,可靠性高;一个线程崩溃可能影响整个程序的稳定性,可靠性较低。 + +* 进程单独占有一定的内存地址空间,进程的创建和销毁不仅需要保存寄存器和栈信息,还需要资源的分配回收以及页调度,开销较大;线程只需要保存寄存器和栈信息,开销较小。 + +另外一个重要区别是,进程是操作系统进行资源分配的基本单位,而线程是操作系统进行调度的基本单位,即CPU分配时间的单位 。 + +#### 独立性 + +Linux系统会给每个进程分配4G的虚拟地址空间(0到3G是User地址空间,3到4G部分是kernel地址空间),进程具备私有的地址空间,未经允许,一个用户进程不能访问其他进程的地址空间。 + +#### 动态性 + +程序是一个静态的指令集合,而进程是正在操作系统中运行的指令集合,进程有自己的生命周期和各种不同的状态。 + +五态模型一般指的是: + +**新建态**(创建一个进程) + +**就绪态**(已经获取到资源,准备好了,进入运行队列,一旦获得时间片可以立即执行) + +**运行态**(获取到了时间片,执行程序) + +**阻塞态**(运行过程中等待获取其他资源,I/O请求等) + +**终止态**(进程被杀死了) + +#### 并发性 + +多个进程可以在CPU上并发执行。 +线程是独立运行和调度的最小单位,线程会共享进程的虚拟空间,一个进程会对应多个线程。在Java中,线程拥有自己私有的程序计数器,虚拟机栈,本地方法栈。 + +#### PS:虚拟内存 + +虚拟内存是一种逻辑上扩充物理内存的技术。基本思想是用软、硬件技术把内存与外存这两级存储器当做一级存储器来用。虚拟内存技术的实现利用了自动覆盖和交换技术。简单的说就是将硬盘的一部分作为内存来使用。 + +#### PS:虚拟地址空间 + +每个进程有4G的地址空间,在运行程序时,只有一部分数据是真正加载到内存中的,内存管理单元将虚拟地址转换为物理地址,如果内存中不存在这部分数据,那么会使用页面置换方法,将内存页置换出来,然后将外存中的数据加入到内存中,使得程序正常运行。 + +### 进程间如何通信? + +进程间通信的方式主要有管道, + +#### 管道 + +调用pipe函数在内存中开辟一块缓冲区,管道半双工的(即数据只能在一个方向上流动),具有固定的读端和写端,调用 + +``` +#include +int pipe(int pipefd[2]); +``` + +### Java中创建线程有哪些方式? + +#### 第一种 继承Thread类,重写Run方法 + +这种方法就是通过自定义CustomThread类继承Thread类,重写run()方法,然后创建CustomThread的对象,然后调用start()方法,JVM会创建出一个新线程,并且为线程创建方法调用栈和程序计数器,此时线程处于就绪状态,当线程获取CPU时间片后,线程会进入到运行状态,会去调用run()方法。并且创建CustomThread类的对象的线程(这里的例子中是主线程)与调用run()方法的线程之间是并发的,也就是在执行run()方法时,主线程可以去执行其他操作。 + +```java +class CustomThread extends Thread { + public static void main(String[] args) { + System.out.println(Thread.currentThread().getName()+"线程调用了main方法"); + for (int i = 0; i < 10; i++) { + if (i == 1) { + CustomThread customThread = new CustomThread(); + customThread.start(); + System.out.println(Thread.currentThread().getName()+"线程--i是"+i); + } + } + System.out.println("main()方法执行完毕!"); + } + void run() { + System.out.println(Thread.currentThread().getName()+"线程调用了run()方法"); + for (int j = 0; j < 5; j++) { + System.out.println(Thread.currentThread().getName()+"线程--j是"+j); + } + System.out.println("run()方法执行完毕!"); + } +} +``` + +输出结果如下: + +``` +main线程调用了main方法 +Thread-0线程调用了run()方法 +Thread-0线程--j是0 +main线程--i是1 +Thread-0线程--j是2 +Thread-0线程--j是3 +Thread-0线程--j是4 +run()方法执行完毕! +main()方法执行完毕! +``` + +可以看到在创建一个CustomThread对象,调用start()方法后,Thread-0调用了run方法,进行for循环,对j进行打印,与此同时,main线程并没有被阻塞,而是继续执行for循环,对i进行打印。 + +##### 执行原理 + +首先我们可以来看看start的源码,首先会判断threadStatus是否为0,如果不为0会抛出异常。然后会将当前对象添加到线程组,最后调用start0方法,因为是native方法,看不到源码,根据上面的执行结果来看,JVM新建了一个线程调用了run方法。 + +```java +private native void start0(); + +public synchronized void start() { + //判断当前Thread对象是否是新建态,否则抛出异常 + if (threadStatus != 0) + throw new IllegalThreadStateException(); + //将当前对象添加到线程组 + group.add(this); + boolean started = false; + try { + start0();//这是一个native方法,调用后JVM会新建一个线程来调用run方法 + started = true; + } finally { + try { + if (!started) { + group.threadStartFailed(this); + } + } catch (Throwable ignore) { + /* do nothing. If start0 threw a Throwable then + it will be passed up the call stack */ + } + } +} +``` +扩展问题:多次调用Thread对象的start()方法会怎么样? + +会抛出IllegalThreadStateException异常。其实在Thread#start()方法里面的的注释中有提到,多次调用start()方法是非法的,所以在上面的start()方法源码中一开始就是对threadStatus进行判断,不为0就会抛出IllegalThreadStateException异常。 + +![image-20200105144159345](../static/image-20200105144159345.png) + +##### 注意事项: + +start()方法中判断threadStatus是否为0,是判断当前线程是否新建态,0是代表新建态(上图中的源码注释里面有提到),而不是就绪态,因为Java的Thread类中,Thread的Runnable状态包括了线程的就绪态和运行态,(Thread的state为RUNNABLE时(也就是threadStatus为4时),代表线程为就绪态或运行态)。执行start()方法的线程还不是JVM新建的线程,所以不是就绪态。有一些技术文章把这里弄错了,例如这一篇[《深入浅出线程Thread类的start()方法和run()方法》](https://juejin.im/post/5b09274af265da0de25759d5) + +![image-20200105144031591](../static/image-20200105144031591.png) + +##### 总结 + +这种方式的缺点很明显,就是需要继承Thread类,而且实际上我们的需求可能仅仅是希望某些操作被一个其他的线程来执行,所以有了第二种方法。 + +#### 第二种 实现Runnable接口 + +这种方式就是创建一个类(例如下面代码中的Target类),实现Runnable接口的Run方法,然后将Target类的实例对象作为Thread的构造器入参target,实际的线程对象还是Thread实例,只不过线程Thread与线程执行体(Target类的run方法)分离了,耦合度更低一些。 + +```java +class ThreadTarget implements Runnable { + void run() { + System.out.println(Thread.currentThread().getName()+"线程执行了run方法"); + } + public static void main(String[] args) { + System.out.println(Thread.currentThread().getName()+"线程执行了main方法"); + ThreadTarget target = new ThreadTarget(); + Thread thread = new Thread(target); + thread.start(); + } +} +``` + +输出结果如下: + +![image-20200105163553969](../static/image-20200105163553969.png) + +##### 原理 + +之所以有这种实现方法,是因为Thread类的run()方法中会判断成员变量target是否为空,不为空就会调用target类的run方法。 + +```java +private Runnable target; +public void run() { + if (target != null) { + target.run(); + } +} +``` + +##### 另外一种写法 + +这种实现方式也有其他的写法,可以不创建Target类。 + +##### 匿名内部类 + +可以不创建Target类,可以使用匿名内部类的方式来实现,因此上面的代码也可以按以下方式写: + +```java +Thread thread = new Thread(new Runnable() { + @Override + public void run() { + System.out.println(Thread.currentThread().getName()+"线程执行了run方法"); + } +}); +thread.start(); +``` +##### Lamda表达式 + +在Java8之后,使用了@FunctionalInterface注解来修饰Runnable接口,表明Runnable接口是一个函数式接口,有且只有一个抽象方法,可以Lambda方式来创建Runnable对象,比使用匿名类的方式更加简洁一些。 + +```java +@FunctionalInterface +public interface Runnable { + public abstract void run(); +} +``` + +因此上面的代码也可以按以下方式写: + +```java +Thread thread = new Thread(()->{ + System.out.println(Thread.currentThread().getName()+"线程执行了run方法"); +}) +thread.start() +``` + +##### 总结 + +这种写法不用继承Thread,但是同样也有缺点,就是线程方法体(也就是run方法)不能设置返回值。 + +#### 第三种 实现Callable接口 + +Runnable接口中的run()方法是没有返回值,如果我们需要执行的任务带返回值就不能使用Runnable接口。创建一个类CallableTarget,实现Callable接口,实现带有**返回值的call()方法**,然后根据CallableTarget创建一个任务FutureTask,然后根据FutureTask来创建一个线程Thread,调用Thread的start方法可以执行任务。 + +```java +public class CallableTarget implements Callable { + public Integer call() throws InterruptedException { + System.out.println(Thread.currentThread().getName()+"线程执行了call方法"); + Thread.sleep(5000); + return 1; + } + public static void main(String[] args) throws ExecutionException, InterruptedException { + System.out.println(Thread.currentThread().getName()+"线程执行了main方法"); + CallableTarget callableTarget = new CallableTarget(); + FutureTask task = new FutureTask(callableTarget); + Thread thread = new Thread(task); + thread.start(); + Integer result = task.get();//当前线程会阻塞,一直等到结果返回。 + System.out.println("执行完毕,打印result="+result); + System.out.println("执行完毕"); + } +} +``` + +原理就是Thread类默认的run()方法实现是会去调用自身实例变量target的run()方法,(target就是我们构造Thread传入的FutureTask),而FutureTask的run方法中就会调用Callable接口的实例的call()方法。 + +```java +//Thread类的run方法实现 +@Override +public void run() { + if (target != null) { + //这里target就是我们在创建Thread时传入的FutureTask实例变量 + target.run(); + } +} +//FutureTask类的run方法实现 +public void run() { + if (state != NEW || + !UNSAFE.compareAndSwapObject(this, runnerOffset, + null, Thread.currentThread())) + return; + try { + Callable c = callable; + if (c != null && state == NEW) { + V result; + boolean ran; + try { + //在这里会调用Callable实例的call方法 + result = c.call(); + ran = true; + } catch (Throwable ex) { + result = null; + ran = false; + setException(ex); + } + if (ran) + set(result); + } + } finally { + // runner must be non-null until state is settled to + // prevent concurrent calls to run() + runner = null; + // state must be re-read after nulling runner to prevent + // leaked interrupts + int s = state; + if (s >= INTERRUPTING) + handlePossibleCancellationInterrupt(s); + } +} +``` + +### Java中的Runnable、Callable、Future、FutureTask的区别和联系? + +最原始的通过新建线程执行任务的方法就是我们去新建一个类,继承Thread,然后去重写run()方法,但是这样限制太大了,Java也不支持多继承。所以有了Runnable。 +##### Runnable +Runnable是一个接口,只需要新建一个类实现这个接口,然后重写run方法,将该类的实例作为创建Thread的入参,线程运行时就会调用该实例的run方法。 +```java +@FunctionalInterfacepublic interface Runnable { + public abstract void run(); +} +``` + +Thread.start()方法->Thread.run()方法->target.run()方法 + +##### Callable + +Callable跟Runnable类似,也是一个接口。只不过它的call方法有返回值,可以供程序接收任务执行的结果。 + +```java +@FunctionalInterfacepublic interface Callable { + V call() throws Exception; +} +``` + +##### Future + +Future也是一个接口,Future就像是一个管理的容器一样,进一步对Runable和Callable的实例进行封装,定义了一些方法。取消任务的cancel()方法,查询任务是否完成的isDone()方法,获取执行结果的get()方法,带有超时时间来获取执行结果的get()方法。 + +```java +public interface Future { + //mayInterruptIfRunning代表是否强制中断 + //为true,如果任务已经执行,那么会调用Thread.interrupt()方法设置中断标识 + //为false,如果任务已经执行,就只会将任务状态标记为取消,而不会去设置中断标识 + boolean cancel(boolean mayInterruptIfRunning); + boolean isCancelled(); + boolean isDone(); + V get() throws InterruptedException, ExecutionException; + V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException; +} +``` + +##### FutureTask + +因为Future只是一个接口,并不能实例化,可以认为FutureTask就是Future接口的实现类,FutureTask实现了RunnableFuture接口,而RunnableFuture接口继承Runnable接口和Future接口。 + +```java +public class FutureTask implements RunnableFuture { +... +} + +public interface RunnableFuture extends Runnable, Future { + void run(); +} +``` + +##### 使用案例 + +使用时,Runnable实现类的实例可以作为Thread的入参使用,而Callable只能使用FutureTask进行封装使用。 + +```java +//Runnable配合Thread进行使用 +Thread threadA = new Thread(new Runnable() { + @Override + public void run() { + //任务的代码 + } + }); + +//Callable使用FutureTask封装后,配合线程池进行使用 +ExecutorService pool = Executors.newSingleThreadExecutor(); +FutureTask task = new FutureTask(new Callable() { + @Override + public Object call() throws Exception { + //任务的代码 + return null; + } +}); +pool.submit(task); + +//Runnable使用FutureTask封装后,配合线程池进行使用 +FutureTask task1 = new FutureTask(new Runnable() { + @Override + public void run() { + //任务的代码 + } +}); +pool.submit(task1); +``` + +### Java中单例有哪些写法? + +正确并且可以做到延迟加载的写法其实就是三种: + +1.使用volatile修饰变量并且双重校验的写法来实现。 + +2.使用静态内部类来实现(类A有一个静态内部类B,类B有一个静态变量instance,类A的getInstance()方法会返回类B的静态变量instance,因为只有调用getInstance()方法时才会加载静态内部类B,这种写法缺点是不能传参。) + +3.使用枚举来实现 + +#### 第1种 不加锁(裸奔写法) + +多线程执行时,可能会在instance完成初始化之前,其他线性线程判断instance为null,从而也执行第二步的代码,导致初始化覆盖。 + +```java +public class UnsafeLazyInitialization { + private static Instance instance; + public static Instance getInstance() { + if (instance == null) //1 + instance = new Instance(); //2 + } + return instance; +} +``` + +#### 第2种-对方法加sychronize锁(俗称的懒汉模式) + +初始化完成以后,每次调用getInstance()方法都需要获取同步锁,导致不必要的开销。 + +```java +public class Singleton { + private static Singleton instance; + public synchronized static Singleton getInstance() { + if (instance == null) + instance = new Instance(); + return instance; + } +} +``` +#### 第3种-使用静态变量(俗称的饿汉模式) + +``` +public class Singleton { + private static Singleton instance = new Singleton(); + public static Singleton getInstance() { + return instance; + } +} +``` +这种方法是缺点在于不能做到延时加载,在第一次调用getInstance()方法之前,如果Singleton类被使用到,那么就会对instance变量初始化。 + +#### 第4种-使用双重检查锁定 + +代码如下: + +```java +public class Singleton { + private static Singleton instance; + public static Singleton getInstance() { + if (instance == null) { + synchronized (Singleton.class) { + if (instance == null) { //双重检查存在的意义在于可能会有多个线程进入第一个判断,然后竞争同步锁,线程A得到了同步锁,创建了一个Singleton实例,赋值给instance,然后释放同步锁,此时线程B获得同步锁,又会创建一个Singleton实例,造成初始化覆盖。 + instance = new Singleton(); + } + } + } + return instance; + } +} +``` + +instance = new Singleton(); + +这句代码在执行时会分解为三个步骤: + +1.为对象分配内存空间。 + +2.执行初始化的代码。 + +3.将分配好的内存地址设置给instance引用。 + +但是编译器会对指令进行重排序,只能保证单线程执行时结果不会变化,也就是可能第3步会在第2步之前执行,某个线程A刚好执行完第3步,正在执行第2步时,此时如果有其他线程B进入if (instance == null)判断,会发现instance不为null,然后将instance返回,但是实际上instance还没有完成初始化,线程B会访问到一个未初始化完成的instance对象。所以需要像第5种解法一样使用volatile来修饰变量,防止重排序。 + +#### 第5种 基于 volatile 的双重检查锁定的解决方案 + +代码如下: + +```java +public class Singleton { + private volatile static Singleton instance; + public static Singleton getInstance() { + if (instance == null) { + synchronized (Singleton.class) { + if (instance == null)//双重检查存在的意义在于可能会有多个线程进入第一个判断,然后竞争同步锁,线程A得到了同步锁,创建了一个Singleton实例,赋值给instance,然后释放同步锁,此时线程B获得同步锁,又会创建一个Singleton实例,造成初始化覆盖。 + instance = new Singleton(); + } + } + return instance; + } +} +``` + +volatile可以保证变量的内存可见性及防止指令重排。 + +volatile修饰的变量在编译后,会多出一个lock前缀指令,lock前缀指令相当于一个内存屏障(内存栅栏),有三个作用: + +* 确保指令重排序时,内存屏障前的指令不会排到后面去,内存屏障后的指令不会排到前面去。 +* 强制对变量在线程工作内存中的修改操作立即写入到物理内存。 +* 如果是写操作,会导致其他CPU中对这个变量的缓存失效,强制其他CPU中的线程在获取变量时从物理内存中获取更新后的值。 + +所以使用volatile修饰后不会出现第3种写法中由于指令重排序导致的问题。 + +#### 第6种 - 使用静态内部类来实现 + +```java +class Test { + public static Signleton getInstance() { + return Signleton.instance ; // 只有调用getInstance()方法时,才会引用到静态内部类Signleton,从而会触发Signleton类的instance变量的初始化,以此实现懒加载的目的。 + } + + private static class Signleton { + private static Signleton instance = new Signleton(); + } +} +``` + +因为JVM底层通过加锁实现,保证一个类只会被加载一次,多个线程在对类进行初始化时,只有一个线程会获得锁,然后对类进行初始化,其他线程会阻塞等待。所以可以使用上面的代码来保证instance只会被初始化一次,这种写法的问题在于创建单例时不能传参。 + +#### 7.使用枚举来实现单例 + +```java +public enum Singleton { + //每个元素就是一个单例 + INSTANCE; + //自定义的一些方法 + public void method(){} +} +``` +这种写法比较简洁,但是不太便于阅读和理解,所以实际开发中应用得比较少,而且由于枚举类是不能通过反射来创建实例的(反射方法newInstance中判断是枚举类型,会抛出IllegalArgumentException异常),所以可以防止反射。而且由于枚举类型的反序列化是通过java.lang.Enum的valueOf方法来实现的,不能自定义序列化方法,可以防止通过序列化来创建多个单例。 + +### 如何解决序列化时可以创建出单例对象的问题? + +如果将单例对象序列化成字节序列后,然后再反序列成对象,那么就可以创建出一个新的单例对象,从而导致单例不唯一,避免发生这种情况的解决方案是在单例类中实现readResolve()方法。 + +```java +public class Singleton implements java.io.Serializable { + + private Object readResolve() { + return INSTANCE; + } +} +``` +通过实现readResolve方法,ObjectInputStream实例对象在调用readObject()方法进行反序列化时,就会判断相应的类是否实现了readResolve()方法,如果实现了,就会调用readResolve()方法返回一个对象作为反序列化的结果,而不是去创建一个新的对象。 + +### volatile关键字有什么用?怎么理解可见性,一般什么场景去用可见性? + +当线程进行一个volatile变量的写操作时,JIT编译器生成的汇编指令会在写操作的指令后面加上一个“lock”指令。 +Java代码如下: +```java +instance = new Singleton(); // instance是volatile变量 +转变成汇编代码,如下。 +0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp); +``` +“lock”有三个作用: + +1.将当前CPU缓存行的数据会写回到系统内存。 + +2.这个写回内存的操作会使得其他CPU里缓存了该内存地址的数据无效。 + +3.确保指令重排序时,内存屏障前的指令不会排到后面去,内存屏障后的指令不会排到前面去。 + +可见性可以理解为一个线程的写操作可以立即被其他线程得知。为了提高CPU处理速度,CPU一般不直接与内存进行通信,而是将系统内存的数据读到内部缓存,再进行操作。对于普通的变量,修改完不知道何时会更新到系统内存。但是如果是对volatile修饰的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在的缓存行的数据立即写回到系统内存。但是即便写回到系统内存,其他CPU中的缓存行数据还是旧的,为了保证数据一致性,其他CPU会嗅探在总线上传播的数据来检查自己的缓存行的值是否过期,当CPU发现缓存行对应的内存地址被修改,那么就会将当前缓存行设置为无效,下次当CPU对这个缓存行上的数据进行修改时,会重新从系统内存中把数据读到处理器缓存里。 + + +##### 使用场景 + +##### 读写锁 + +如果需要实现一个读写锁,每次只能一个线程去写数据,但是有多个线程来读数据,就synchronized同步锁来对set方法加锁,get方法不加锁, 使用volatile来修饰变量,保证内存可见性,不然多个线程可能会在变量修改后还读到一个旧值。 + +```java +volatile Integer a; +//可以实现一写多读的场景,保证并发修改数据时的正确。 +set(Integer c) { + synchronized(this.a) { + this.a = c; + + } +} +get() { + return a; +} +``` + +##### 状态位 + +用于做状态位标志,如果多个线程去需要根据一个状态位来执行一些操作,使用volatile修饰可以保证内存可见性。 + +用于单例模式用于保证内存可见性,以及防止指令重排序。 + + +### Java中线程的状态是怎么样的? + +在操作系统中,线程等同于轻量级的进程。 + +![img](../static/4621.png) + +所以传统的操作系统线程一般有以下状态 + +1. **新建状态**: + 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。 + +2. **就绪状态**: + 当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。 + +3. **运行状态:** + 如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。 + +4. **阻塞状态:** + + 如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种: + + - 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。 + - 同步阻塞:线程在获取 synchronized同步锁失败(因为同步锁被其他线程占用)。 + - 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。 + +5. **死亡状态:** + 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。 + +但是Java中Thread对象的状态划分跟传统的操作系统线程状态有一些区别。 + +```java +public enum State { + NEW,//新建态 + RUNNABLE,//运行态 + BLOCKED,//阻塞态 + WAITING,//等待态 + TIMED_WAITING,//有时间限制的等待态 + TERMINATED;//死亡态 +} +``` + +![线程状态图](../static/watermark.jpeg) + + + +#### NEW 新建态 + +处于NEW状态的线程此时尚未启动,还没调用Thread实例的start()方法。 + +#### RUNNABLE 运行态 + +表示当前线程正在运行中。处于RUNNABLE状态的线程可能在Java虚拟机中运行,也有可能在等待其他系统资源(比如I/O)。 + +> Java线程的**RUNNABLE**状态其实是包括了传统操作系统线程的**ready**和**running**两个状态的。 + +#### BLOCKED 阻塞态 + +阻塞状态。线程没有申请到synchronize同步锁,就会处于阻塞状态,等待锁的释放以进入同步区。 + +#### WAITING 等待态 + +等待状态。处于等待状态的线程变成RUNNABLE状态需要其他线程唤醒。 + +调用如下3个方法会使线程进入等待状态: + +- Object.wait():使当前线程处于等待状态直到另一个线程调用notify唤醒它; +- Thread.join():等待线程执行完毕,底层调用的是Object实例的wait()方法; +- LockSupport.park():除非获得调用许可,否则禁用当前线程进行线程调度。 + +#### TIMED_WAITING 超时等待状态 + +超时等待状态。线程等待一个具体的时间,时间到后会被自动唤醒。 + +调用如下方法会使线程进入超时等待状态: + +- Thread.sleep(long millis):使当前线程睡眠指定时间; + +- Object.wait(long timeout):线程休眠指定时间,等待期间可以通过notify()/notifyAll()唤醒; + +- Thread.join(long millis):等待当前线程最多执行millis毫秒,如果millis为0,则会一直执行; + +- LockSupport.parkNanos(long nanos): 除非获得调用许可,否则禁用当前线程进行线程调度指定时间; + +- LockSupport.parkUntil(long deadline):同上,也是禁止线程进行调度指定时间; + +#### TERMINATED 终止态 + +终止状态。此时线程已执行完毕。 + +#### 状态转换 + +1.BLOCKED与RUNNABLE状态的转换 + +处于BLOCKED状态的线程是因为在等待锁的释放,当获得锁之后就转换为RUNNABLE状态。 + +2.WAITING状态与RUNNABLE状态的转换 + +**Object.wait()**,**Thread.join()**和**LockSupport.park()**这3个方法可以使线程从RUNNABLE状态转为WAITING状态。 + +3.TIMED_WAITING与RUNNABLE状态转换 + +TIMED_WAITING与WAITING状态类似,只是TIMED_WAITING状态等待的时间是指定的。 + +调用**Thread.sleep(long)**,**Object.wait(long)**,**Thread.join(long)**会使得RUNNABLE状态转换为TIMED_WAITING状态 + +### wait(),join(),sleep()方法有什么作用? + +首先需要对wait(),join(),sleep()方法进行介绍。 + +#### Object.wait()方法是什么? + +调用wait()方法前线程必须持有对象Object的锁。线程调用wait()方法后,会释放当前的Object锁,进入锁的monitor对象的等待队列,直到有其他线程调用notify()/notifyAll()方法唤醒等待锁的线程。 + +需要注意的是,其他线程调用notify()方法只会唤醒单个等待锁的线程,如果有多个线程都在等待这个锁的话,不一定会唤醒到之前调用wait()方法的线程。 + +同样,调用notifyAll()方法唤醒所有等待锁的线程之后,也不一定会马上把时间片分给刚才放弃锁的那个线程,具体要看系统的调度。 + +#### Thread.join()方法是什么? + +join()方法是Thread类的一个实例方法。它的作用是让当前线程陷入“等待”状态,等join的这个线程threadA执行完成后,再继续执行当前线程。 + +实现原理是join()方法本身是一个sychronized修饰的方法,也就是调用join()这个方法需要先获取threadA的锁,获得锁之后再调用wait()方法来进行等待,一直到threadA执行完成后,threadA会调用notify_all()方法,唤醒所有等待的线程,当前线程才会结束等待。 + +```java +Thread threadA = new Thread(); +threadA.join(); +``` + +join()方法的源码: + +```java +public final void join() throws InterruptedException { + join(0);//0的话代表没有超时时间一直等下去 +} +public final synchronized void join(long millis) +throws InterruptedException { + long base = System.currentTimeMillis(); + long now = 0; + + if (millis < 0) { + throw new IllegalArgumentException("timeout value is negative"); + } + + if (millis == 0) { + while (isAlive()) { + wait(0); + } + } else { + while (isAlive()) { + long delay = millis - now; + if (delay <= 0) { + break; + } + wait(delay); + now = System.currentTimeMillis() - base; + } + } +} +``` + +这是jvm中Thead的源码,在线程执行结束后会调用notify_all来唤醒等待的线程。 + +```java +//一个c++函数: +void JavaThread::exit(bool destroy_vm, ExitType exit_type) ; +//里面有一个贼不起眼的一行代码 +ensure_join(this); + +static void ensure_join(JavaThread* thread) { + Handle threadObj(thread, thread->threadObj()); + + ObjectLocker lock(threadObj, thread); + + thread->clear_pending_exception(); + + java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); + java_lang_Thread::set_thread(threadObj(), NULL); + //同志们看到了没,别的不用看,就看这一句 + //thread就是当前线程,是啥?就是刚才例子中说的threadA线程 + lock.notify_all(thread); + thread->clear_pending_exception(); +} +``` + +#### sleep()方法是什么? + +sleep方法是Thread类的一个静态方法。它的作用是让当前线程睡眠一段时间。:**sleep方法是不会释放当前线程持有的锁,而wait方法会。** + +sleep与wait方法的区别: + +- wait可以指定时间,也可以不指定;而sleep必须指定时间。 +- wait释放cpu资源,同时释放锁;sleep释放cpu资源,但是不释放锁,所以易死锁。(调用join()方法也不会释放锁) +- wait必须放在同步块或同步方法中,而sleep可以再任意位置。 + +参考文章: + +http://redspider.group:4000/article/01/4.html + +https://www.jianshu.com/p/5d88b122a050 + +### Thread.sleep(),Object.wait(),LockSupport.park()有什么区别? + +1.这三个方法都会让线程挂起,释放CPU时间片,进入到阻塞态。但是Object.wait()需要释放锁,所以必须在synchronized同步锁中使用,同理配套的Object.notify()也是。而Thead.sleep(),LockSupport.park()不需要在synchronized同步锁中使用,并且在调用时也不会释放锁。 + +2.由于Thread.sleep()没有对应的唤醒线程的方法,所以必须指定超时时间,超过时间后,线程恢复。所以调用Thread.sleep()后的线程一般是出于TIME_WAITING状态,而调用了Object.wait(),LockSupport.park()的方法是进入到WAITING状态。 + +3.Object.wait()对应的唤醒方法为Object.notify(),LockSupport.park()对应的唤醒方法为LockSupport.unpark()。 + +4.在代码中必须能保证wait方法比notify方法先执行,如果notify方法比wait方法早执行的话,就会导致因wait方法进入休眠的线程接收不到唤醒通知的问题。而park、unpark则不会有这个问题,我们可以先调用unpark方法释放一个许可证,这样后面线程调用park方法时,发现已经许可证了,就可以直接获取许可证而不用进入休眠状态了。(**LockSupport.park() 的实现原理是通过二元信号量做的阻塞,要注意的是,这个信号量最多只能加到1,也就是无论执行多少次unpark()方法,也最多只会有一个许可证。**) + +5.三种方法让线程进入阻塞态后,都可以响应中断,也就是调用Thread.interrupt()方法会设置中断标志位,之前执行Thread.sleep(),Object.wait()了的线程会抛出InterruptedException异常,然后需要代码进行处理。而调用了park()方法的线程在响应中断只会相当于一次正常的唤醒操作(等价于调用unpark()方法),让线程唤醒,继续执行后面的代码,不会抛出InterruptedException异常。 + +![img](../static/5bff9535e4b04dd2799a6ae8.png) + +参考链接: + +https://blog.csdn.net/u013332124/article/details/84647915 + +### 谈一谈你对线程中断的理解? + +在Java中认为,一个线程不应该由其他线程来强制中断或者停止,所以一些会强制中断线程的方法Thread.stop(), Thread.suspend()方法都已经废弃了。所以一般是通过调用thread.interrupt();方法来设置线程的中断标识, + +1.这样如果线程是处于阻塞状态,会抛出InterruptedException异常,代码可以进行捕获,进行一些处理。(例如Object#wait、Thread#sleep、BlockingQueue#put、BlockingQueue#take。其中BlockingQueue主要调用conditon.await()方法进行等待,底层通过LockSupport.park()实现) + +2.如果线程是处于RUNNABLE状态,也就是正常运行,调用thread.interrupt();只是会设置中断标志位,不会有什么其他操作。 + +```java +//将线程的中断标识设置为true +thread.interrupt(); +//判断线程的中断标识是否为true +thread.isInterrupted() +//会返回当前的线程中断状态,并且重置线程的中断标识,将中断标识设置为false +thread.interrupted() +``` + +### 线程执行的任务可以终止吗? + +##### 1.设置中断 + +FutureTask提供了cancel(boolean mayInterruptIfRunning)方法来取消任务,并且 + +如果入参为false,如果任务已经在执行,那么任务就不会被取消。 + +如果入参为true,如果任务已经在执行,那么会调用Thread的interrupt()方法来设置线程的中断标识,如果线程处于阻塞状态,会抛出InterruptedException异常,如果正常状态只是设置标志位,修改interrupted变量的值。所以如果要取消任务只能在任务内部中调用thread.isInterrupted()方法获取当前线程的中断状态,自行取消。 + +##### 2.线程的stop方法 + +线程的stop()方法可以让线程停止执行,释放所有的锁,抛出ThreadDeath这种Error。但是在释放锁之后,没有办法让受这些锁保护的资源,对象处于一个安全,一致的状态。(例如有一个变量a,本来的值是0,你的线程任务是将a++后然后再进行a--。正常情况下任务执行完之后,其他线程取到这个变量a的值应该是0,但是如果之前调用了Thread.stop方法时,正好是在a++之后,那么变量a就会是1,这样其他线程取到的a就是出于不一致的状态。) + +### 让线程顺序执行有哪些方法? + +##### 1.主线程Join + +就是调用threadA.start()方法让线程A先执行,然后主线程调用threadA.join()方法,然后主线程进入TIME_WAITING状态,直到threadA执行结束后,主线程才能继续往下执行,执行线程B的任务。(join方法的底层实现其实是调用了threadA的wait()方法,当线程A执行完毕后,会自动调用notifyAll()方法唤醒所有线程。) + +示例代码如下: + +```java +Thread threadA = new Thread(new Runnable() { + @Override + public void run() { + //执行threadA的任务 + } +}); +Thread threadB= new Thread(new Runnable() { + @Override + public void run() { + //执行threadB的任务 + } +}); +//执行线程A任务 +threadA.start(); +//主线程进行等待 +threadA.join(); +//执行线程B的任务 +threadB.start(); +``` + +##### 子线程Join + +就是让线程B的任务在执行时,调用threadA.join()方法,这样就只有等线程A的任务执行完成后,才会执行线程B。 + +```java + Thread threadA = new Thread(new Runnable() { + @Override + public void run() { + //执行threadA的任务 + } + }); + Thread threadB= new Thread(new Runnable() { + @Override + public void run() { + //子线程进行等待,知道threadA任务执行完毕 + threadA.join(); + //执行threadB的任务 + } + }); + //执行线程A任务 + threadA.start(); + //执行线程B的任务 + threadB.start(); +``` +##### 单线程池法 + +就是使用Executors.newSingleThreadExecutor()这个线程池,这个线程池的特点就是只有一个执行线程,可以保证任务按顺序执行。 + +```java +ExecutorService pool = Executors.newSingleThreadExecutor(); +//提交任务A +executorService.submit(taskA); +//提交任务B +executorService.submit(taskB); +``` + +##### 等待通知法(wait和notify) + +就是在线程B中调用Object.waiting()方法进行等待,线程A执行完毕后调用Object.notify()方法进行唤醒。(这种方法有两个缺点,一个是Object.waiting()和notify()方法必须在同步代码块中调用,第二个是如果线程A执行过快,先调用了object.notify()方法,就会导致线程B后面一直得不到唤醒。) + +```java + final Object object = new Object(); + Thread threadA = new Thread(new Runnable() { + @Override + public void run() { + //执行threadA的任务 + synchronized(object) { + object.notify(); + } + } + }); + Thread threadB= new Thread(new Runnable() { + @Override + public void run() { + synchronized(object) { + //子线程进行等待,知道threadA任务执行完毕 + object.wait(); + //执行threadB的任务 + } + } + }); +``` + +##### 等待通知法(await和singal) + +具体实现就是Reentrantlock可以创建出一个Condition实例queue,可以认为是一个等待队列,线程B调用queue.await()就会进行等待,直到线程A执行完毕调用queue.signal()来唤醒线程B。 + +```java + final ReentrantLock lock = new ReentrantLock(); + final Condition queue1 = lock.newCondition(); + final Object object = new Object(); + final Thread threadA = new Thread(new Runnable() { + @Override + public void run() { + //执行threadA的任务 + lock.lock(); + try { + //唤醒线程B的任务 + queue1.signal(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("执行了任务A2"); + lock.unlock(); + } + }); + final Thread threadB= new Thread(new Runnable() { + @Override + public void run() { + lock.lock(); + //子线程进行等待,知道threadA任务执行完毕 + try { + queue1.await(); + System.out.println("执行了任务B2"); + + } catch (InterruptedException e) { + e.printStackTrace(); + } + //执行threadB的任务 + lock.unlock(); + } + }); + threadA.start(); + threadB.start(); +``` + + + +参考链接: + +http://cnblogs.com/wenjunwei/p/10573289.html + +### 线程间怎么通信? + +#### 1.synchronized锁 + +通过synchronized锁来进行同步,让一次只能一个线程来执行。 + +#### 2.等待/通知机制 + +```java +//假设我们的需求是B执行结束后A才能执行 +//线程A的代码 +synchronized(对象) { while(条件不满足) { + while(条件不满足) { + 对象.wait(); //线程A进行等待 + } + //线程A执行相关的的逻辑 + } + +//线程B的代码 + synchronized(对象) { + //线程B执行相关的的逻辑 + //线程B唤醒线程A + 对象.notifyAll(); + } + +``` + +等待/通知机制,是指一个线程A调用了对象objectA的wait()方法进入等待状态,而另一个线程B调用了对象objectA的notify()或者notifyAll()方法,线程A收到通知后从对象objectA的wait()方法返回,进而执行后续操作。上述两个线程通过对象objectA来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。 + +![image-20200518195123985](../static/image-20200518195123985.png) + +1)使用wait()、notify()和notifyAll()时需要先对调用对象加锁。 + +2)调用wait()方法后,线程状态由RUNNING变为WAITING,并将当前线程放置到对象的 + +等待队列。 + +3)notify()或notifyAll()方法调用后,等待线程依旧不会从wait()返回,因为等待线程只是从等待队列到了同步队列,需要调用notify()或 notifAll()的线程释放锁之后,等待线程获得锁,才能从同步队列中移除,才有机会从wait()返回,才能继续往下执行。 + +4)notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll() 方法则是将等待队列中所有的线程全部移到同步队列,被移动的线程状态由WAITING变为 BLOCKED。 + +5)从wait()方法返回的前提是获得了调用对象的锁。 + +![image-20200518195448708](../static/image-20200518195448708.png) + +##### 3.管道 + +管道输入/输出流 管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要 用于线程之间的数据传输,而传输的媒介为内存。 管道输入/输出流主要包括了如下4种具体实现:PipedOutputStream、PipedInputStream、 PipedReader和PipedWriter,前两种面向字节,而后两种面向字符。 + + PipedReader和PipedWriter可以一个线程A调用PipedWriter实例的write()方法,往里面写数据,然后与PipedWriter实例建立连接的PipedReader实例可以读到数据,线程B可以通过PipedReader实例读到数据。 + +在代码清单4-12所示的例子中,创建了printThread,它用来接受main线程的输入,任何 main线程的输入均通过PipedWriter写入,而printThread在另一端通过PipedReader将内容读出并打印。 + +代码清单4-12 Piped.java +```java +public class Piped { + public static void main(String[] args) throws Exception { + PipedWriter out = new PipedWriter(); + PipedReader in = new PipedReader(); + // 将输出流和输入流进行连接,否则在使用时会抛出IOException out.connect(in); + Thread printThread = new Thread(new Print(in), "PrintThread"); printThread.start(); + int receive = 0; + try { + while ((receive = System.in.read()) != -1) { out.write(receive); + } + } finally { out.close(); } + } + static class Print implements Runnable { + private PipedReader in; + public Print(PipedReader in) { + this.in = in; + } + public void run() { + int receive = 0; + try { + while ((receive = in.read()) != -1) { System.out.print((char) receive); + } + } catch (IOException ex) { + } + } + } +} +``` + +运行该示例,输入一组字符串,可以看到被printThread进行了原样输出。 + +Repeat my words. +Repeat my words. + +#### 4.Thread.join + +Thread.join()的使用如果一个线程A执行了thread.join()语句,当前线程A会一直等待thread线程终止之后才从thread.join()返回,向下执行。线程Thread除了提供join()方法之外,还提供了join(long millis)和join(long millis,int nanos)两个具备超时参数的方法。 + +#### 5.ThreadLocal的使用 + +ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这 个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个 线程上的一个值。 + +```java +public class Profiler { + // 第一次get()方法调用时会进行初始化(如果set方法没有调用),每个线程会调用一次 + private static final ThreadLocal TIME_THREADLOCAL = new ThreadLocal() { + protected Long initialValue() { return System.currentTimeMillis();} + }; + public static final void begin() { + TIME_THREADLOCAL.set(System.currentTimeMillis()); + } + public static final long end() { + return System.currentTimeMillis() - TIME_THREADLOCAL.get(); + } + public static void main(String[] args) throws Exception { + Profiler.begin(); + TimeUnit.SECONDS.sleep(1); + System.out.println("Cost: " + Profiler.end() + " mills"); + } +} +``` + +Profiler可以被复用在方法调用耗时统计的功能上,在方法的入口前执行begin()方法,在 + +方法调用后执行end()方法,好处是两个方法的调用不用在一个方法或者类中,比如在AOP(面 向方面编程)中,可以在方法调用前的切入点执行begin()方法,而在方法调用后的切入点执行 end()方法,这样依旧可以获得方法的执行耗时。 + +### 怎么实现实现一个生产者消费者? + +#### 1.使用Object.wait()和Object.notify()实现 +使用queue作为一个队列,存放数据,并且使用Synchronized同步锁,每次只能同时存在一个线程来生产或者消费数据, + +生成线程发现队列容量>10,生产者线程就进入waiting状态,一旦成功往队列添加数据,那么就唤醒所有线程(主要是生产者线程起来消费)。 + +消费线程消费时,发现队列容量==0,也会主动进入waiting状态。 + +伪代码如下: + +```java +LinkedList queue = new LinkedList<>(); +void produce(Integer value) { + synchronized(queue) {//加锁控制,保证同一时间点,只能有一个线程生成或者消费 + while(queue.size()>10) { + queue.waiting(); + } + queue.add(value); + //唤醒消费者线程 + queue.notifyAll(); + } +} +Integer consumer() { + synchronized(queue) {//加锁控制,保证同一时间点,只能有一个线程生成或者消费 + while(queue.size()==0) { + queue.waiting(); + } + Integer value = queue.poll(); + //唤醒生产者线程 + queue.notifyAll(); + return value; + } +} +``` + +完整代码如下: + +```java +public static void main(String[] args) { + Queue queue = new LinkedList<>(); + final Customer customer = new Customer(queue); + final Producer producer = new Producer(queue); + ExecutorService pool = Executors.newCachedThreadPool(); + for (int i = 0; i < 1000; i++) { + pool.execute(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Integer a = customer.removeObject(); + System.out.println("消费了数据 "+a); + } + }); + pool.execute(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Random random = new Random(); + Integer a = random.nextInt(1000); + System.out.println("生成了数据 "+a); + producer.addObject(a); + } + }); + } +} +private static class Customer { + Queue queue; + Customer(Queue queue) { this.queue = queue; } + public Integer removeObject() { + synchronized (queue) { + try { + while (queue.size()==0) { + System.out.println("队列中没有元素了,进行等待"); + queue.wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + Integer number = queue.poll(); + System.out.println("唤醒所有生产线程,当前queue大小是" + queue.size()); + queue.notifyAll(); + return number; + } + } +} +private static class Producer { + Queue queue; + Producer(Queue queue) { this.queue = queue; } + public void addObject(Integer number) { + synchronized (queue) { + try { + while (queue.size()>10) { + queue.wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + queue.add(number); + queue.notifyAll(); + System.out.println("唤醒所有消费线程,当前queue大小是"+queue.size()); + } + } +} +``` + +#### 2.使用Lock和Condition来实现 + +调用Object.wait()方法可以让线程进入等待状态,被添加到Object的monitor监视器的等待队列中,Object.notifyAll()可以唤醒monitor监视器等待队列中的所有线程。 + +而调用lock的newCondition()方法,可以返回一个ConditionObject实例对象,每个ConditionObject包含一个链表,存储等待队列。可以认为一个ReentrantLock有一个同步队列(存放没有获得锁的线程),和多个等待队列(存放调用await()方法的线程)。使用Condition.singal()和Condition.singalAll()可以更加**精准的唤醒线程**,也就是唤醒的都是这个Condition对应的等待队列里面的线程,而Object.notify()和Object.notifyAll()只能唤醒等待队列中的所有的线程。 + +```java +ReentrantLock lock = new ReentrantLock(); +Condition customerQueue = lock.newCondition(); +``` +ReentrantLock的Condition相关的实现 + +![img](../static/640-5667220.jpeg) + +```java +abstract static class Sync extends AbstractQueuedSynchronizer { + final ConditionObject newCondition() { + return new ConditionObject(); + } +} +//AQS内部类 ConditionObject +public class ConditionObject implements Condition, java.io.Serializable { + private static final long serialVersionUID = 1173984872572414699L; + //链表头结点 + private transient Node firstWaiter; + //链表尾结点 + private transient Node lastWaiter; + //真正的创建Condition对象 + public ConditionObject() { } +} +``` + +消费者-生产者实现 + +```java +public static void main(String[] args) { + ReentrantLock lock = new ReentrantLock(); + Condition customerQueue = lock.newCondition(); + Condition producerQueue = lock.newCondition(); + + Queue queue = new LinkedList<>(); + final Customer customer = new Customer(lock,customerQueue, producerQueue,queue); + final Producer producer = new Producer(lock,customerQueue, producerQueue,queue); + + ExecutorService pool = Executors.newCachedThreadPool(); + for (int i = 0; i < 1000; i++) { + pool.execute(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Integer a = customer.take(); +// System.out.println("消费了数据 "+a); + } + }); + pool.execute(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Random random = new Random(); + Integer a = random.nextInt(1000); +// System.out.println("生成了数据 "+a); + producer.add(a); + } + }); + } +} + +private static class Customer { + private ReentrantLock lock; + private Condition customer; + private Condition producer; + private Queue queue; + + Customer(ReentrantLock lock, Condition customer, Condition producer,Queue queue) { + this.lock = lock; + this.customer = customer; + this.producer = producer; + this.queue = queue; + } + + public Integer take() { + lock.lock(); + Integer element = null; + try { + while (queue.size() == 0) { + customer.await(); + } + element = queue.poll(); + System.out.println("消费者线程取出来元素"+element); + producer.signalAll(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + lock.unlock(); + } + return element; + } +} + +private static class Producer { + private ReentrantLock lock; + private Condition customer; + private Condition producer; + private Queue queue; + + Producer(ReentrantLock lock, Condition customer, Condition producer,Queue queue) { + this.lock = lock; + this.customer = customer; + this.producer = producer; + this.queue = queue; + } + + public void add( Integer element) { + lock.lock(); + try { + while (queue.size() > 10) { + producer.await(); + } + queue.add(element); + System.out.println("生成和线程添加元素"+element); + customer.signalAll(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + lock.unlock(); + } + } +} +``` +#### 3.使用BlockingQueue实现 +利用阻塞队列BlockingQueue的特征进行生产和消费的同步(其实阻塞队列内部也是基于Lock,condition实现的 ) + +```java +public class BlockQueueRepository extends AbstractRepository implements Repository { + public BlockQueueRepository(int cap) { + //cap代表队列的最大容量 + products = new LinkedBlockingQueue<>(cap); + } + + @Override + public void put(T t) { + if (isFull()) { + log.info("repository is full, waiting for consume....."); + } + try { + //如果队列长度已满,那么会阻塞等待 + ((BlockingQueue) products).put(t); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Override + public T take() { + T product = null; + if (isEmpty()) { + log.info("repository is empty, waiting for produce....."); + } + try { + //如果队列元素为空,那么也会阻塞等待 + product = (T) ((BlockingQueue) products).take(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return product; + } +} +``` + +### 谈一谈你对线程池的理解? + +#### 首先线程池有什么作用? + +* 1.提高响应速度,如果线程池有空闲线程的话,可以直接复用这个线程执行任务,而不用去创建。 + +* 2.减少资源占用,每次都创建线程都需要申请资源,而使用线程池可以复用已创建的线程。 + +* 3.可以控制并发数,可以通过设置线程池的最大线程数量来控制最大并发数,如果每次都是创建新线程,来了大量的请求,可能会因为创建的线程过多,造成内存溢出。 + +* 4.更加方便来管理线程资源。 + +#### 线程池有哪些参数? + +```java +public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + ThreadFactory threadFactory) { + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + threadFactory, defaultHandler); + } +``` + +##### 1.corePoolSize 核心线程数 + +该线程池中**核心线程数最大值**,添加任务时,即便有空闲线程,只要当前线程池线程数()); +} +public LinkedBlockingQueue() { + this(Integer.MAX_VALUE); +} +``` + +一句话总结就是:**线程数固定,等待队列无限长**。 + +创建一个线程池,核心线程数与最大线程数值都是传入参数nThreads。可控制线程最大并发数,超出的线程会在队列中等待(比较适合需要控制并发量的情况)。主要是通过将核心线程数设置为与最大线程数相等实现的。缺点是LinkedBlockingQueue队列的默认长度是Integer.MAX_VALUE,也存在内存溢出的风险。 + +**与CachedThreadPool的区别**: + +- 因为 corePoolSize == maximumPoolSize ,所以**newFixedThreadPool**只会创建核心线程。 而**CachedThreadPool**因为corePoolSize=0,所以只会创建非核心线程。 +- 在 getTask() 方法,如果队列里没有任务可取,线程会一直阻塞在 LinkedBlockingQueue.take() ,线程不会被回收。 **CachedThreadPool**的线程会在60s后收回。 +- 由于线程不会被回收,会一直卡在阻塞,所以**没有任务的情况下, FixedThreadPool占用资源更多**。 +- 都几乎不会触发拒绝策略,但是原理不同。FixedThreadPool是因为阻塞队列可以很大(最大为Integer最大值),故几乎不会触发拒绝策略;CachedThreadPool是因为线程池很大(最大为Integer最大值),几乎不会导致线程数量大于最大线程数,故几乎不会触发拒绝策略。 + +##### newSingleThreadExecutor 单线程池 + +```java +public static ExecutorService newSingleThreadExecutor() { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue())); + } +``` +一句话总结就是:**单线程池,等待队列无限长。** +创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。主要是通过将核心线程数和最大线程数都设置为1来实现。 + +##### newCachedThreadPool可缓存线程池 + +创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。但是由于最大线程数设置的是Integer.MAX_VALUE,存在内存溢出的风险。 + +一句话总结就是:**最大线程数无限大,线程超时被回收** + +```java +public static ExecutorService newCachedThreadPool() { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); +} +``` + +`CacheThreadPool`的**运行流程**如下: + +1. 提交任务进线程池。 +2. 因为**corePoolSize**为0的关系,不创建核心线程,线程池最大为Integer.MAX_VALUE。 +3. 尝试将任务添加到**SynchronousQueue**队列。(需要注意的是**SynchronousQueue**本身不存储任务,只是将添加任务的线程加入一个栈中,进行阻塞等待,然后线程池中的线程空闲时,会从栈中取出线程,取出线程携带的任务,进行执行。) +4. 如果**SynchronousQueue**入列成功,等待被当前运行的线程空闲后拉取执行。如果当前没有空闲线程,那么就创建一个非核心线程,然后从SynchronousQueue拉取任务并在当前线程执行。 +5. 如果**SynchronousQueue**已有任务在等待,入列操作将会阻塞。 + +当需要执行很多**短时间**的任务时,CacheThreadPool的线程复用率比较高, 会显著的**提高性能**。而且线程60s后会回收,意味着即使没有任务进来,CacheThreadPool并不会占用很多资源。 + +##### newScheduledThreadPool定时执行线程池 + +创建一个定时执行的线程池,主要是通过DelayedWorkQueue来实现(该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素)。支持定时及周期性任务执行。但是由于最大线程数设置的是Integer.MAX_VALUE,存在内存溢出的风险。 + +一句话总结就是:线程数无限大,定时执行。 + +```java +public ScheduledThreadPoolExecutor(int corePoolSize, + ThreadFactory threadFactory) { + super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, + new DelayedWorkQueue(), threadFactory); +} +``` + +##### 为什么不建议大家使用Executors的四种线程池呢? + +主要是newFixedThreadPool和newSingleThreadExecutor的等待队列是LinkedBlockingQueue,长度是Integer.MAX_VALUE,,可以认为是无限大的,如果创建的任务特别多,可能会造成内存溢出。而newCachedThreadPool和newScheduledThreadPool的最大线程数是Integer.MAX_VALUE,如果创建的任务过多,可能会导致创建的线程过多,从而导致内存溢出。 + +扩展资料: + +[Java线程池实现原理及其在美团业务中的实践](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html) + +[SynchronousQueue实现原理](https://zhuanlan.zhihu.com/p/29227508) + +### 线程池有哪些状态? + +线程池生命周期: + +- **RUNNING**:表示线程池处于运行状态,这时候的线程池可以接受任务和处理任务。值是-1, + +- **SHUTDOWN**:表示线程池不接受新任务,但仍然可以处理队列中的任务,二进制值是0。调用showdown()方法会进入到SHUTDOWN状态。 + +- **STOP**:表示线程池不接受新任务,也不处理队列中的任务,同时中断正在执行任务的线程,值是1。调用showdownNow()方法会进入到STOP状态。 + +- **TIDYING**:表示所有的任务都已经终止,并且工作线程的数量为0。值是2。SHUTDOWN和STOP状态的线程池任务执行完了,工作线程也为0了就会进入到TIDYING状态。 + +- **TERMINATED**:表示线程池处于终止状态。值是3 + + ![img](../static/640-20200728210136673.jpeg) + +### 怎么根据业务场景确定线程池的参数corePoolSize和maximumPoolSize? + +#### 方法一 计算密集型任务 +因为是计算密集型任务,可以理解为每个任务在执行期间基本没有IO操作,全部都在CPU时间片中执行。所以可以理解为CPU就是满载的,CPU利用率就是100%,其实线程数等于CPU数就可以的,但是由于需要考虑到计算密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,此时应该需要有一个“额外”的空闲线程来获得时间片,然后执行,可以确保在这种情况下CPU周期不会中断工作,充分利用CPU。 +```java +最佳线程数=CPU的数量+1 +``` +#### 方法二 IO密集型任务 +这种任务在执行时,需要进行一些IO操作,所以为了充分利用CPU,应该在线程进行IO操作时,就让出时间片,CPU进行上下文切换,执行其他线程的任务,保证CPU利用率尽可能达到100%。 + +如果任务有50%的时间需要CPU执行状态,其他时间进行IO操作,则程序所需线程数为CPU数量的1除以0.5,也就是2倍。如果任务有20%的时时间需要CPU执行,其他时间需要进行IO操作,最佳线程数也就是1除以0.2,也就是CPU数的5倍。 +所以公式为 + +```java +最佳线程数 = CPU数量/(每个任务中需要CPU执行的时间的比例) += CPU数量/(CPU运行时间/任务执行总时间)=CPU数量/(CPU运行时间/(CPU运行时间+IO操作时间)) +所以最终公式为 +最佳线程数/CPU数量 = CPU运行时间/(CPU运行时间+IO操作时间) +``` +##### 不足 +但是在实际线上运行的环境中,每个任务执行的时间是各不相同的,而且我们其实是不太方便去监测每个任务执行时需要的CPU执行时间,IO操作时间的,所以这种方法只是一种理论。 + +#### 方法三 动态化线程池 +这种其实是美团他们做的一个线程池监测平台,主要把任务分成两种, +##### 追求响应时间的任务 +一种是追求响应时间的任务,例如使用线程池对发起多个网络请求,然后对结果进行计算。 这种任务的最大线程数需要设置大一点,然后队列使用同步队列,队列中不缓存任务,任务来了就会被执行。判断线程池资源不够用时,一般是发现活跃线程数/最大线程数>阀值(默认是0.8)时,或者是线程池抛出的RejectedExecut异常次数达到阀值,就会进行告警。然后程序员收到告警后,动态发送修改核心线程数,最大线程数,队列相关的指令,服务器进行动态修改。 + +##### 追求高吞吐量的任务 + +假设说需要定期自动化生成一些报表,不需要考虑响应时间,只是希望如何使用有限的资源,尽可能在单位时间内处理更多的任务,也就是吞吐量优先的问题。 +这种就是使用有界队列,对任务进行缓存,然后线程进行并发执行。判断线程池资源不够用时,一般是发现等待队列中的任务数量/等待队列的长度>阀值(默认是0.8)时,或者是线程池抛出的RejectedExecut异常次数达到阀值,就会进行告警。然后程序员收到告警后,动态发送修改核心线程数,最大线程数,队列相关的指令,服务器进行动态修改。 + +ThreadPoolExecutor提供了如下几个public的setter方法 + +![image-20210119104549770](../static/image-20210119104549770.png) + +调用corePoolSize方法之后,线程池会直接覆盖原来的corePoolSize值,并且基于当前值和原始值的比较结果采取不同的处理策略。(总得来说就是,多退少补的策略) + +**对于新corePoolSize<当前工作线程数的情况:** + +说明有多余的worker线程,此时会向当前idle状态的worker线程发起中断请求以实现回收,多余的worker在下次idel的时候也会被回收。 + +**对于新corePoolSize>当前工作线程数且队列中有任务的情况:** + +如果当前队列中有待执行任务,则线程池会创建新的worker线程来执行队列任务。 + +setCorePoolSize的方法的执行流程入下图所示: + + + +![图20 setCorePoolSize方法执行流程](../static/9379fe1666818237f842138812bf63bd85645.png) + +扩展资料: +[Java并发(八)计算线程池最佳线程数](https://www.cnblogs.com/jpfss/p/11016169.html) + +### ThreadLocal是什么?怎么避免内存泄露? + +从字面意思上,ThreadLocal会被理解为线程本地存储,就是对于代码中的一个变量,每个线程拥有这个变量的一个副本,访问和修改它时都是对副本进行操作。 +##### 使用场景: +ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景。(例如:方法直接调用时传递的变量过多,为了代码简洁性,可以使用ThreadLocal,在前一个方法中,将变量进行存储,后一个方法中取,进行使用。) +```java +public class A { + // 每个线程本地副本初始化 + private static ThreadLocal threadLocal = new ThreadLocal <>(). withInitial (() -> new UserData ()); + public static void setUser (UserLogin user){ + if (user == null ) + return ; + UserData userData = threadLocal.get(); + userData. setUserLogin (user); + } + public static UserLogin getUser (){ + return threadLocal.get(). getUserLogin (); + } +} +``` + +##### 实现原理 + +就是每个Thread有一个ThreadLocalMap,类似于HashMap,当调用ThreadLocal#set()方法进行存值时,实际上是先获取到当前的线程,然后获取线程的map,是一个ThreadLocalMap类型,然后会在这个map中添加一个新的键值对,key就是我们ThreadLocal变量的地址,value就是我们存的值。ThreadLocalMap与HashMap不同的时,解决HashMap使用的是**开放定址法**,也就是当发现hashCode计算得到数组下标已经存储了元素后,会继续往后找,直到找到一个空的数组下标,存储键值对。 + +```java +//ThreadLocal实例的赋值方法 +public void set(T value) { + //获取当前线程 + Thread t = Thread.currentThread(); + //获取线程对应的Map + ThreadLocalMap map = getMap(t); + //将值存入线程特有的Map中 + if (map != null) + //key为this就是当前ThreadLocal引用变量的地址 + //value就是我们要存储的值 + map.set(this, value); + else + createMap(t, value); +} +ThreadLocalMap getMap(Thread t) { + //线程的threadLocals实例变量就是Map + return t.threadLocals; +} +``` + +##### ThreadLocal中的Entry的key使用了弱引用,为什么使用弱引用? + +![thread](../static/thread.png) + +首先在上面类A的代码中,类A中有一个ThreadLocal类型的变量 + +它们的引用链如下: + +```java +ThreadLocal变量所在的类的实例(代码中A的实例)->ThreadLocal +执行代码的线程->线程独有的ThreadLocalMap->引用的key就是ThreadLocal +``` + +可以看到ThreadLocal变量不仅被所在的类A的实例所引用,还被执行的线程所引用, + +1.如果使用强引用,也就是线程对ThreadLocal变量是强引用,那么即便实例A被回收了,只要线程还没有被回收,线程的ThreadLocalMap还会引用这个key(也就是这个ThreadLocal遍历),导致这个key 没有被回收,造成内存泄露。 + +2.如果使用弱引用,不会影响key的回收,也就是不会影响引用了ThreadLocal的实例对象的回收。 + +但是即便使用弱引用,ThreadLocalMap对value的引用是强引用(一边value是局部变量,也不能用弱引用,那样在用到的时候就会被),但是value依然不会被回收,会造成内存泄露。 + +通常来说,value回收的时机有两个: + +1.我们在用完ThreadLocal后,应该遵循规范手动调用ThreadLocal#remove()对键值对value释放,这样可以使value被回收。 + +2.此线程在其他对象中使用ThreadLocal对线程ThreadLocalMap进行set()和get()时,由于需要进行开放定址法进行探测,会对沿途过期的键值对(就是key为null的键值对)进行清除。以及set()方法触发的cleanSomeSlots()方法对过期键值对进行清除。 + +[《一篇文章,从源码深入详解ThreadLocal内存泄漏问题》](https://www.jianshu.com/p/dde92ec37bd1) + +### Random类取随机数的原理是什么? + +首先在初始化Random实例的时候就会根据当前的时间戳生成一个种子数seed。 + +```java +public Random() { + this(seedUniquifier() ^ System.nanoTime()); + } + + private static long seedUniquifier() { + // L'Ecuyer, "Tables of Linear Congruential Generators of + // Different Sizes and Good Lattice Structure", 1999 + for (;;) { + long current = seedUniquifier.get(); + long next = current * 181783497276652981L; + if (seedUniquifier.compareAndSet(current, next)) + return next; + } + } +``` + +然后每次取随机数时是拿seed乘以一个固定值multiplier,作为随机数。 + +```java + protected int next(int bits) { + long oldseed, nextseed; + AtomicLong seed = this.seed; + do { + oldseed = seed.get(); + nextseed = (oldseed * multiplier + addend) & mask; + } while (!seed.compareAndSet(oldseed, nextseed)); + return (int)(nextseed >>> (48 - bits)); + } +``` + +但是这样的话,多线程并发使用Math.random取随机数时,同一个时间点取到的随机数一样的概率会比较大。所以可以使用ThreadLocalRandom.current().nextInt()方法去取随机数。每个线程第一次调用ThreadLocalRandomd的current()方法时,会为这个线程生成一个线程独立的种子数seed,这样多线程并发读取随机数时,可以保证取到的随机数都是不一样的。 + +```java +public static ThreadLocalRandom current() { + //判断这个线程是否生成种子 + if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) + localInit(); + return instance; + } + +//为这个线程生成一个种子seed,并且将种子seed,和线程已生成种子的标志 存储到Unsafe类中 +static final void localInit() { + int p = probeGenerator.addAndGet(PROBE_INCREMENT); + int probe = (p == 0) ? 1 : p; // skip 0 + long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT)); + Thread t = Thread.currentThread(); + UNSAFE.putLong(t, SEED, seed); + UNSAFE.putInt(t, PROBE, probe); + } + +//获取随机数,根据当前种子计算随机数 +public int nextInt(int origin, int bound) { + if (origin >= bound) + throw new IllegalArgumentException(BadRange); + return internalNextInt(origin, bound); + } + +final int internalNextInt(int origin, int bound) { + int r = mix32(nextSeed()); + if (origin < bound) { + int n = bound - origin, m = n - 1; + if ((n & m) == 0) + r = (r & m) + origin; + else if (n > 0) { + for (int u = r >>> 1; + u + m - (r = u % n) < 0; + u = mix32(nextSeed()) >>> 1) + ; + r += origin; + } + else { + while (r < origin || r >= bound) + r = mix32(nextSeed()); + } + } + return r; + } +``` + +### 僵尸进程,孤儿进程,守护进程是什么? + +僵尸进程:通常来说,使用fork()系统调用从一个父进程创建出一个子进程,子进程退出,是需要父进程调用wait()或者是waitpid()函数来回收子进程的资源,如果父进程没有调用,子进程的信息就会一直在内存中,而不会被回收,变成僵尸进程。 + +孤儿进程:就是父进程先退出了,它的子进程会被init进程接管,由它来收集子进程的状态。(init进程是内核启动时,创建出来的进程,是一个以root身份运行的普通用户进程,是永远不会停止的。) + +守护进程是脱离于终端并且在后台运行的进程,脱离终端是为了避免将在执行的过程中的信息打印在终端上,并且进程也不会被任何终端所产生的终端信息所打断。 + +### BlockingQueue的原理是怎么样的? + +https://www.cnblogs.com/tjudzj/p/4454490.html + +### 进程间通信的方式 + +https://network.51cto.com/art/201911/606827.htm?mobile + diff --git a/docs/Kafka.md b/docs/Kafka.md new file mode 100644 index 0000000..f3a4529 --- /dev/null +++ b/docs/Kafka.md @@ -0,0 +1,253 @@ +### kafka的工作流程是怎么样的? + +1.首先一个kafka集群有很多个kafka的服务器,每个kafka服务器就是一个broker,每一类消息有一个topic,生产者将一个消息发送给broker。 + +2.每个topic会有一个或者多个分区,broker根据分发机制将这个消息分给这个topic下的某个分区的leader, + +分发机制: + +* 1.发的消息指定了分区就发到特定分区下 + +* 2.指定了key,就根据murmur2 哈希算法对key计算得到一个哈希值,将哈希值与分区数量取余,得到分区。 + +* 3.没有指定分区,也没有指定key,那么就根据一个自增计数与分区数取余得到分区,这样可以让消息分发在每个分区更加均匀。 + +3.每个分区就是一个目录,目录名是topic+分区编号,在收到消息后会将消息写入到日志文件中,如果一个分区的消息都有存放在一个日志文件中,那么文件会比较大,查询时会比较慢,而且也不便于之后删除旧的消息。所以每个分区对应多个大小相等的segment文件,每个segment的名称是上一个segment最后一条消息的offset,一个segment有两个文件,一个是.index文件,记录了消息的offset及这条消息数据在log文件中的偏移量。一个是.log文件,实际存储每个消息数据,每条消息数据大小不一,每条消息数据包含offset,消息体大小,消息体等等内容。查的时候根据offset先去index文件找到偏移量,然后去log文件中读。 + +(具体的segment切分有很多个触发条件: + +当log文件>log.segment.bytes时切分,默认是1G。 + +或者是segment文件中最早的消息距离现在的时间>log.roll.ms配置的时间,默认是7天。 + +或者是索引文件index>log.index.size.max.bytes的大小,默认是10M。) + +4.分区leader将消息存储到日志文件中后还不能算是写成功,会把消息同步给所有follower,当follower同步好消息之后就会给leader发ack,leader收到所有follower返回的ack之后,这条才算是写成功,然后才会给生产者返回写成功。(依据ACK配置来决定多少follower同步成功才算生产者发送消息成功) + +5.消费者读数据时就去分区的leader中去读,一个消费者可以消费多个分区,但是一个分区只能一个消费者来消费,默认消费者取完数据就会自动提交,一般会关闭自动提交,消费者消费成功后,进行手动提交,分区的offset才会向后移动。(默认是会自动提交,一般会关闭自动提交) + +##### 注意事项: + +1.replication.factor>=2,也就是一个分区至少会有两个副本。 + +2.min.insync.replicas默认是1,leader至少要有一个follow跟自己保持联系没有掉线。(这个配置只有在ack为all或者-1时有用,也就是ack为all也只是要求生产者发送的消息,被leader以及ISR集合里面的从节点接收到,就算所有节点都接收到了。) + +3.一般设置了ack=all就不会丢数据。因为会保证所有的follower都收到消息,才算broker接收成功,默认ack=1。 + +4.retries=,生产者写入消息失败后的重试次数。 + +5.每个partition有一个offset, + +6.生产者ACK配置: + +**1(默认)** 数据发送到Kafka后,经过leader成功接收消息的的确认,就算是发送成功了。在这种情况下,如果leader宕机了,则会丢失数据。 + +**0** 生产者将数据发送出去就不管了,不去等待任何返回。这种情况下数据传输效率最高,但是数据可靠性确是最低的。 + +**-1** 也就是all,producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。 + +### 怎么防止Kafka 丢数据? + +这块比较常见的一个场景,就是 `Kafka` 某个 `broker` 宕机,然后重新选举 `partition` 的 `leader` 。大家想想,要是此时其他的 `follower` 刚好还有些数据没有同步,结果此时 `leader` 挂了,然后选举某个 `follower` 成 `leader` 之后,不就少了一些数据?这就丢了一些数据啊。 + +此时一般是要求起码设置如下 4 个参数: + +- 给 `topic` 设置 `replication.factor` 参数:这个值必须大于 1,要求每个 `partition` 必须有 **至少** 2 个副本。 +- 在 `Kafka` 服务端设置 `min.insync.replicas` 参数:这个值必须大于 1,这个是 **要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系**,没掉队,这样才能确保 `leader` 挂了还有一个 `follower` 吧。 +- 在 `producer` 端设置 `acks=all`:这个是要求每条数据,**必须是写入所有 replica 之后,才能认为是写成功了**。 +- 在 `producer` 端设置 `retries=MAX`(很大很大很大的一个值,无限次重试的意思):这个是要求**一旦写入失败,就无限重试**,卡在这里了。 + +这样配置之后,至少在Kafka `broker` 端就可以保证在`leader` 所在 `broker` 发生故障,进行`leader` 切换时,数据不会丢失。 + +### 生产者会不会弄丢数据? + +如果按照上述的思路设置了`acks=all`,一定不会丢,要求是,你的 `leader` 接收到消息,所有的`follower` 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者可以自动不断的重试,重试无限次。 + +#### 怎么实现 Exactly-Once? + +##### 生产端幂等性发送 + +为了实现Producer的幂等语义,Kafka引入了`Producer ID`(即`PID`)和`Sequence Number`。每个新的Producer在初始化的时候会被分配一个唯一的PID,该PID对用户完全透明而不会暴露给用户。 + +对于每个PID,该Producer发送数据的每个``都对应一个从0开始单调递增的`Sequence Number`。 + +类似地,Broker端也会为每个``维护一个序号,并且每次Commit一条消息时将其对应序号递增。对于接收的每条消息,如果其序号比Broker维护的序号(即最后一次Commit的消息的序号)大一,则Broker会接受它,否则将其丢弃: + +- 如果消息序号比Broker维护的序号大一以上,说明中间有数据尚未写入,也即乱序,此时Broker拒绝该消息,Producer抛出`InvalidSequenceNumber` +- 如果消息序号小于等于Broker维护的序号,说明该消息已被保存,即为重复消息,Broker直接丢弃该消息,Producer抛出`DuplicateSequenceNumber` + +上述设计解决了0.11.0.0之前版本中的两个问题: + +- Broker保存消息后,发送ACK前宕机,Producer认为消息未发送成功并重试,造成数据重复 + +- 前一条消息发送失败,后一条消息发送成功,前一条消息重试后成功,造成数据乱序。 + + http://www.jasongj.com/kafka/transaction/ + +##### 消费端幂等性 + +只能自己从业务层面保证重复消费的幂等性,例如引入版本号机制。 + +#### 事务性保证 + +上述幂等设计只能保证单个Producer对于同一个``的`Exactly Once`语义。 + +另外,它并不能保证写操作的原子性——即多个写操作,要么全部被Commit要么全部不被Commit。 + +更不能保证多个读写操作的的原子性。尤其对于Kafka Stream应用而言,典型的操作即是从某个Topic消费数据,经过一系列转换后写回另一个Topic,保证从源Topic的读取与向目标Topic的写入的原子性有助于从故障中恢复。 + +事务保证可使得应用程序将生产数据和消费数据当作一个原子单元来处理,要么全部成功,要么全部失败,即使该生产或消费跨多个``。 + +另外,有状态的应用也可以保证重启后从断点处继续处理,也即事务恢复。 + +为了实现这种效果,应用程序必须提供一个稳定的(重启后不变)唯一的ID,也即`Transaction ID`。`Transactin ID`与`PID`可能一一对应。区别在于`Transaction ID`由用户提供,而`PID`是内部的实现对用户透明。 + +另外,为了保证新的Producer启动后,旧的具有相同`Transaction ID`的Producer即失效,每次Producer通过`Transaction ID`拿到PID的同时,还会获取一个单调递增的epoch。由于旧的Producer的epoch比新Producer的epoch小,Kafka可以很容易识别出该Producer是老的Producer并拒绝其请求。 + +有了`Transaction ID`后,Kafka可保证: + +- 跨Session的数据幂等发送。当具有相同`Transaction ID`的新的Producer实例被创建且工作时,旧的且拥有相同`Transaction ID`的Producer将不再工作。 +- 跨Session的事务恢复。如果某个应用实例宕机,新的实例可以保证任何未完成的旧的事务要么Commit要么Abort,使得新实例从一个正常状态开始工作。 + +需要注意的是,上述的事务保证是从Producer的角度去考虑的。从Consumer的角度来看,该保证会相对弱一些。尤其是不能保证所有被某事务Commit过的所有消息都被一起消费,因为: + +- 对于压缩的Topic而言,同一事务的某些消息可能被其它版本覆盖 +- 事务包含的消息可能分布在多个Segment中(即使在同一个Partition内),当老的Segment被删除时,该事务的部分数据可能会丢失 +- Consumer在一个事务内可能通过seek方法访问任意Offset的消息,从而可能丢失部分消息 +- Consumer可能并不需要消费某一事务内的所有Partition,因此它将永远不会读取组成该事务的所有消息 + +### 消息队列的使用场景有哪些? + +1. **异步通信**:有些业务不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。 +2. **解耦**:降低工程间的强依赖程度,针对异构系统进行适配。在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。通过消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口,当应用发生变化时,可以独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束 +3. **冗余**:有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。 +4. **扩展性**:因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。不需要改变代码、不需要调节参数。便于分布式扩容 +5. **过载保护**:在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量无法提取预知;如果以为了能处理这类瞬间峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃 +6. **可恢复性**:系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。 +7. **顺序保证**:在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。 +8. **缓冲**:在任何重要的系统中,都会有需要不同的处理时间的元素。消息队列通过一个缓冲层来帮助任务最高效率的执行,该缓冲有助于控制和优化数据流经过系统的速度。以调节系统响应时间。 +9. **数据流处理**:分布式系统产生的海量数据流,如:业务日志、监控数据、用户行为等,针对这些数据流进行实时或批量采集汇总,然后进行大数据分析是当前互联网的必备技术,通过消息队列完成此类数据收集是最好的选择 + +##### MQ缺点 + +1. 系统可用性降低:系统引入的外部依赖越多,越容易挂掉。本来你就是 `A` 系统调用 `BCD` 三个系统的接口就好了, `ABCD` 四个系统好好的,没啥问题,你偏加个 `MQ` 进来,万一 `MQ` `挂了咋整,MQ` 一挂,整套系统崩溃的,你不就完了?如何保证消息队列的高可用。 +2. 系统复杂度提高:硬生生加个 `MQ` 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。 +3. 一致性问题: `A` 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 `BCD` 三个系统那里, `BD` 两个系统写库成功了,结果 `C` 系统写库失败了,咋整?你这数据就不一致了。 + +#### ISR是什么? + +ISR(in-sync replica) 就是 Kafka 为某个分区维护的一组同步集合,即每个分区都有自己的一个 ISR 集合,就是从分区的从节点中找出一些节点加入到ISR集合(min.insync.replicas这个参数设定ISR中的最小副本数是多少,默认值为1)。处于 ISR 集合中的副本,意味着 follower 副本与 leader 副本保持同步状态,只有处于 ISR 集合中的副本才有资格被选举为 leader。follower从leader同步数据有一些延迟(延迟时间replica.lag.time.max.ms),一旦超过延迟时间,就会把这个这个follower从ISR列表中移除。被移除的followe会从leader复制数据进行追赶,一旦追赶上又可以重新进入ISR列表。一条 Kafka 消息,只有被 ISR 中的副本都接收到,才被视为“已同步”状态。这跟 zk 的同步机制不一样,zk 只需要超过半数节点写入,就可被视为已写入成功。 + +### 什么是零拷贝技术? +传统的IO接口像read和write系统调用,在执行过程中都是涉及到数据拷贝操作的,比如调用read()接口去读取一个文件时,首先需要将CPU由用户切换成内核态,然后把文件从磁盘读取到 + +#### read()和write() + +![图片](../static/640) + +**read()系统调用的步骤:** +1.会涉及到到一次用户态到内核态的切换,然后会发出 sys_read()系统调用,从文件读取数据。(一次上下文切换) +2.磁盘控制器会使用DMA技术将磁盘文件拷贝到内核内存空间的缓冲区。(一次DMA拷贝) +3.CPU会将数据从内核内存空间的缓冲区拷贝到用户进程内存空间的缓冲区。(一次CPU拷贝) +4.然后read()系统调用返回后,会进行内核态往用户态的切换,这样用户程序进程就可以修改数据了。(一次上下文切换) + +**write()系统调用的步骤:** +1.首先会涉及CPU从用户态切换到内核态,然后会将数据从用户程序的内存空间拷贝到内核内存空间中的Socket缓冲区。(一次上下文切换,一次CPU拷贝) +2.网卡会使用DMA技术,将数据从内核内存空间中的缓冲区拷贝到网卡。(一次DMA拷贝) +3.write()调用完成后会从内核态切换到用户态。(一次上下文切换) + +#### 2.MMAP和write() + +![图片](../static/640-20210326173442637) + +##### mmap + +1.CPU从用户态切换到内核态,磁盘控制器使用DMA技术将数据从磁盘拷贝到内核的内存空间。不会将数据拷贝到用户程序的内存空间,而是将一块物理内存让用户进程的空间与内核空间进行共享,将内核中的这部分内存空间映射到用户进程的内存空间,从而让用户进程可以直接访问这部分内存。(一次上下文切换,一次DMA拷贝) + +2.mmap调用完毕后,CPU会从内核态切换到用户态。(一次上下文切换) + +mmap相比于read()系统调用还是会有2次上下文切换,但是可以减少一次CPU拷贝,因为数据是存在内核的内存空间中。 + +##### write + +1.首先CPU从用户态切换到内核态,然后把数据从内核的内存空间拷贝到内核中Socket缓冲区。(一次上下文切换,一次CPU拷贝) + +2.网卡使用DMA技术,将数据从Socket缓冲区拷贝到网卡。发送完毕后,从内核态切换为用户态。(一次上下文切换,一次DMA拷贝) + +https://mp.weixin.qq.com/s/xDZ9NnyUZSoR9npuMLdpWA +https://blog.csdn.net/choumu8867/article/details/100658332 + +#### sendfile + +这种方式只能用于发送文件,不能修改文件,在Kakfa发送消息给消费者时有用到。 + +![图片](../static/640-20210326191349615) + +读取时: + +1.首先CPU从用户态切换成内核态,然后磁盘控制器使用DMA技术将文件从磁盘拷贝到内核空间的缓冲区中。 + +(一次上下文切换,一次DMA拷贝) + +发送时: + +2.早期的版本是将数据从内核空间中的缓存区拷贝到内核空间的Socket缓冲区,在Linux 2.4以后,是只需要将数据在内核空间的文件数据缓存中的位置和偏移量写入到Socket缓存中,然后网卡直接从Socket缓存中读取文件的位置和偏移量,使用DMA技术拷贝到网卡。发送完毕后,从内核态切换为用户态。 + +(一次上下文切换,一次DMA拷贝。) + +##### 总结: + +传统read()和write()方案:数据拷贝了4次,CPU上下文切换了很多次 + +mmap和write()方案:数据拷贝了3次,会减少一次CPU拷贝,上下文切换了4次。(可以减少1次CPU拷贝) + +sendfile方案:数据拷贝了2次,上下文切换了2次。但是用户进程不能修改数据。(可以减少2次CPU拷贝,至少2次上下文切换) + +### Kafka刷盘时机是怎么样的? +log.flush.interval.messages 最大刷盘消息数量 +log.flush.interval.interval.ms 最大刷盘时间间隔 +log.flush.scheduler.interval.ms 定期刷盘间隔 +可以通过设置 最大刷盘消息数量 和 最大刷盘时间间隔 来控制fsync系统调用的时间,但是Kafka不推荐去设置这些参数,希望让操作系统来决定刷盘的时机,这样可以支持更高的吞吐量。而且Kafka保证可用性是通过多副本来实现的,一个机器挂掉了就会选举副本作为leader。 +### Kafka什么时候进行rebalance? +1.topic下分区的数量增加了或者减少了。(这个一般是我们手动触发的) + +2.消费者的数量发生了改变,例如新增加了消费者或者有消费者挂掉了。 +Kafka有一个session.timeout.ms,最大会话超时时间,最长是10s。就是如果broker与消费者之间的心跳包超过10s还没有收到回应,就会认为消费者掉线了。以及还有一个max.poll.interval.ms参数,消费者两次去broker拉取消息的间隔,默认是5分钟。如果消费者两次拉取消息的间隔超过了5分钟,就会认为消费者掉线了。 + +一旦发生rebalance了,有可能会导致重复消费的问题,就是消费者A拉取了100条消息,消费时间超过了5分钟,被broker认定下线,就会进行rebalance,把这个分区分配给其他消费者消费,其他消费者就会进行重复消费。 + +怎么解决rebalance带来的重复消费问题呢? + +1.可以减少每批消息的处理时间,让每条消息的处理时间减少,或者是修改max.poll.records,减小每次拉取消息的数量。 + +2.可以自行在MySQL或者Redis里面存储每个分区消费的offset,然后消费者去一个新的分区拉取消息时先去读取上次消费的offset。 + +3.为消息分配一个唯一的消息id,通过消息id来判定是否重复消费了。 + +##### kafka 1.1的优化 + +新版本新增了**group.initial.rebalance.delay.ms**参数。空消费组接受到成员加入请求时,不立即转化到PreparingRebalance状态来开启reblance。当时间超过**group.initial.rebalance.delay.ms**后,再把group状态改为PreparingRebalance(开启reblance),这样可以避免服务启动时,consumer陆续加入引起的频繁Rebalance。 + +##### Kafka2.3对reblance的优化 + +但对于运行过程中,consumer超时或重启引起的reblance则无法避免,其中一个原因就是,consumer重启后,它的身份标识会变。简单说就是Kafka不确认新加入的consumer是否是之前挂掉的那个。 + +在Kafka2.0中引入了静态成员ID,使得consumer重新加入时,可以保持旧的标识,这样Kafka就知道之前挂掉的consumer又恢复了,从而不需要Reblance。这样做的好处有两个: + +1. 降低了Kafka Reblance的频率 +2. 即使发生Reblance,Kafka尽量让其他consumer保持原有的partition,减少了重分配引来的耗时、幂等等问题 + +https://blog.csdn.net/weixin_37968613/article/details/104607012 + +https://blog.csdn.net/z69183787/article/details/105138782 + +https://zhuanlan.zhihu.com/p/87577979 + +https://www.cnblogs.com/runnerjack/p/12108132.html + +### kafka的选举机制 + +https://blog.csdn.net/qq_37142346/article/details/91349100 + +https://honeypps.com/mq/kafka-basic-knowledge-of-selection/ + diff --git a/docs/LeetCode.md b/docs/LeetCode.md new file mode 100644 index 0000000..5b5c42a --- /dev/null +++ b/docs/LeetCode.md @@ -0,0 +1,2514 @@ +## LeetCode 热门100题-题解(上) + +##### 主要是记录自己刷题的过程,也方便自己复习 + +##### [第1题-两数之和](#第1题-两数之和) +##### [第206题-反转链表](#第206题-反转链表) + +##### [第2题-两数相加](#第2题-两数相加) +##### [第3题-无重复字符的最长子串](#第3题-无重复字符的最长子串) +##### [第20题-有效的括号](#第20题-有效的括号) +##### [第5题-最长回文子串](#第5题-最长回文子串) +##### [第19题-删除链表的倒数第N个节点](#第19题-删除链表的倒数第N个节点) +##### [第121题-买卖股票的最佳时机](#第121题-买卖股票的最佳时机) +##### [第70题-爬楼梯](#第70题-爬楼梯) +##### [第53题-最大子序和](#第53题-最大子序和) +##### [第21题-合并两个有序链表](#第21题-合并两个有序链表) +##### [第283题-移动零](#第283题-移动零) +##### [第34题-在排序数组中查找元素的第一个和最后一个位置](#第34题-在排序数组中查找元素的第一个和最后一个位置) +##### [第11题-盛最多水的容器](#第11题-盛最多水的容器) +##### [第17题-电话号码的字母组合](#第17题-电话号码的字母组合) +##### [第15题-三数之和](#第15题-三数之和) +##### [第141题-环形链表](#第141题-环形链表) +##### [第104题-二叉树的最大深度](#第104题-二叉树的最大深度) +##### [第22题-括号生成](#第22题-括号生成) +##### [第102题-二叉树的层序遍历](#第102题-二叉树的层序遍历) +##### [第198题-打家劫舍](#第198题-打家劫舍) +##### [第46题-全排列](#第46题-全排列) +##### [第55题-跳跃游戏](#第55题-跳跃游戏) +##### [第62题-不同路径](#第62题-不同路径) +##### [第56题-合并区间](#第56题-合并区间) +##### [第169题-多数元素](#第169题-多数元素) +##### [第101题-对称二叉树](#第101题-对称二叉树) +##### [第136题-只出现一次的数字](#第136题-只出现一次的数字) +##### [第23题-合并K个升序链表](#第23题-合并K个升序链表) +##### [第94题-二叉树的中序遍历](#第94题-二叉树的中序遍历) +##### [第64题-最小路径和](#第64题-最小路径和) +##### [第215题- 数组中的第K个最大元素](#第215题-数组中的第K个最大元素) + +##### [第234题- 回文链表](#第234题-回文链表) +##### [第200题-岛屿数量](#第200题-岛屿数量) +##### [第48题-旋转图像](#第48题-旋转图像) +##### [第98题-验证二叉搜索树](#第98题-验证二叉搜索树) +##### [第78题-子集](#第78题-子集) +##### [第75题-颜色分类](#第75题-颜色分类) +##### [第39题-组合总和](#第39题-组合总和) +##### [第226题-翻转二叉树](#第226题-翻转二叉树) +##### [第31题-下一个排列](#第31题-下一个排列) +##### [第322题-零钱兑换](#第322题-零钱兑换) +##### [第300题-最长递增子序列](#第300题-最长递增子序列) + +### 第1题-两数之和 + +#### 题目描述 +题目详情:https://leetcode-cn.com/problems/two-sum/ +给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + +你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。 + + ``` +示例: +给定 nums = [2, 7, 11, 15], target = 9 +因为 nums[0] + nums[1] = 2 + 7 = 9 +所以返回 [0, 1] + ``` + +#### 思路 + +其实也没啥好说的,就是对数组进行遍历,遍历时将target-nums[i]得到需要的值needValue,判断hashMap中是否存在这个needValue,存在就直接返回了,不存在就将nums[i]添加到hashMap,继续遍历。 + +```java +import java.util.HashMap; +class Solution { + HashMap hashMap = new HashMap(); + public int[] twoSum(int[] nums, int target) { + if(nums==null||nums.length==0) {return null;} + for(int i = 0;i2->3->4->5->NULL +输出: 5->4->3->2->1->NULL +``` +#### 解题思路 + +循环的思路就是遍历节点,保存每个节点的下一个节点,然后将当前节点的next指针指向上一个节点,一直反转到最后。需要注意的地方就是需要将原链表头结点head的next指针置为null,否则会形成环。 + +#### 循环的解法: + +```java +class Solution { + public ListNode reverseList(ListNode head) { + if(head==null||head.next==null){return head;} + ListNode preNode = head; + ListNode currentNode = head.next; + //将原来头结点的next指针设置为null + head.next = null; + while(currentNode!=null) { + //保存指向下一个节点的指针 + ListNode saveNode = currentNode.next; + //将当前节点的next指向前一个节点 + currentNode.next = preNode; + preNode = currentNode; + currentNode = saveNode; + } + return preNode; + } +} +``` + +#### 递归解法 + +就是一直递归,然后将每个节点的next节点的next指针指向当前节点 + +```java +ListNode newHead = null; +public static ListNode reverseList(ListNode node) { + if(node==null){return node;} + if(node.next==null) {//说明是旧链表的尾节点,设置为新链表的头 + newHead = node; + return newHead; + } + ListNode nextNode = node.next; + reverseList(nextNode); + nextNode.next = node; + //这个其实主要是为了将旧链表的头结点设置为null + node.next = null; + return newHead; +} +``` +### 第3题-无重复字符的最长子串 +题目详情:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/ + +#### 题目描述: + +给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。 + +示例 1: + +输入: "abcabcbb" +输出: 3 +解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 +示例 2: + +输入: "bbbbb" +输出: 1 +解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 +示例 3: + +输入: "pwwkew" +输出: 3 +解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 + 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。 + +#### 解题思路 + +就是用滑动窗口,初始left指针指向第一个元素,right指针指向第二个元素,然后在while循环中判断,set中是否包含right指针当前的字符(set会包含left到right之间所有的字符) + +1.包含,说明之前窗口已经出现了right指针当前的字符,那么从set中移除left指针对应的字符,然后left指针右移。 + +2.不包含,说明之前窗口没有出现right指针当前的字符,那么更新最大窗口值max,并且right指针右移。 + +```java +public int lengthOfLongestSubstring(String s) { + if(s==null||s.length()==0) { + return 0; + } + HashSet set = new HashSet(); + int left = 0; + int right = 1; + int max=1; + char[] array = s.toCharArray(); + set.add(array[0]); + while(right max ? right - left + 1:max; + right++; + } + } + return max; +} +``` +### 第2题-两数相加 +题目详情:https://leetcode-cn.com/problems/add-two-numbers/ + +#### 题目详情: + +给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照逆序的方式存储的,并且它们的每个节点只能存储 一位 数字。 + +如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 + +您可以假设除了数字 0 之外,这两个数都不会以 0 开头。 + +示例: + +输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) +输出:7 -> 0 -> 8 +原因:342 + 465 = 807 + +#### 解题思路 + +有点像是合并链表,不过合并时是将两个原链表的节点值相加,得到一个新节点的值,主要需要考虑相加时的进位问题。如果链表还没有遍历完,进位只需要将carryFlag设置为1,下次循环时,计算sum时加1就好了,主要是当存在一个链表遍历完了时,这里采取的策略是: + +* 1.如果carryFlag为0,不需要考虑进位,将另外一个链表添加到新链表后面,结束循环。 +* 2.如果carryFlag为1,需要考虑进位, + * 2.1两个链表都为空,那么建一个值为1的节点添加到新链表最后面,结束循环。 + * 2.2链表l1为空,那么一个值为1的节点到链表l1的末尾,继续循环。 + * 2.3链表l2为空,那么一个值为1的节点到链表l2的末尾,继续循环。 + +```java +public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + ListNode head = new ListNode(-1); + ListNode currentNode = head; + int carryFlag = 0; + while(l1!=null&&l2!=null) { + int sum = l1.val+l2.val; + if(carryFlag==1) { + sum++; + carryFlag=0; + } + if(sum>=10) { + sum = sum%10; + carryFlag = 1; + } + ListNode node = new ListNode(sum); + currentNode.next = node; + currentNode = node; + if(l1.next == null || l2.next == null) {//将后面的拼过来 + if(carryFlag == 0) {//没有进位,直接将剩余链表接过来 + currentNode.next = l1.next == null ? l2.next : l1.next; + break; + } else { + //两个链表都到末尾了,并且有进位就建新节点 + if(l1.next == null && l2.next == null) { + currentNode.next = new ListNode(1); + break; + } else if(l1.next == null) {//只是链表1后面没有节点了,添加一个新节点到链表1后面 + l1.next = new ListNode(1); + carryFlag=0; + } else {//只是链表2后面没有节点了,添加一个新节点到链表1后面 + l2.next = new ListNode(1); + carryFlag=0; + } + } + } + l1=l1.next; + l2=l2.next; + } + return head.next; +} +``` + +### 第20题-有效的括号 +题目详情:https://leetcode-cn.com/problems/valid-parentheses/ + +#### 题目描述 + +给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 + +有效字符串需满足: + +左括号必须用相同类型的右括号闭合。 +左括号必须以正确的顺序闭合。 +注意空字符串可被认为是有效字符串。 + +```java +示例 1: + +输入: "()" +输出: true +示例 2: + +输入: "()[]{}" +输出: true +示例 3: + +输入: "(]" +输出: false +示例 4: + +输入: "([)]" +输出: false +示例 5: + +输入: "{[]}" +输出: true +``` + +#### 解题思路 + +就是遍历字符串, + +字符属于左括号就添加到栈中, + +字符属于右括号就判断是否属于与栈顶元素对应,是的话可以将栈顶出栈,不是的话就说明不匹配,返回 false。遍历完成需要判断栈的长度是否为0,不为0代表还存在没有匹配上的左括号,不满足要求。 + +```java +class Solution { + public boolean isValid(String s) { + char[] array = s.toCharArray(); + HashMap map = new HashMap(); + map.put('(',')'); + map.put('[',']'); + map.put('{','}'); + Stack stack = new Stack(); + for(int i = 0 ; i < array.length ; i++) { + char c = array[i]; + if(map.containsKey(c)) { + stack.push(map.get(c)); + } else { + if (stack.size() > 0 && c == stack.peek()) { + stack.pop(); + } else { + return false; + } + } + } + return stack.size() == 0 ? true : false; + } +} +``` + + +### 第5题-最长回文子串 +题目详情:https://leetcode-cn.com/problems/longest-palindromic-substring/ + +#### 题目详情 + +给定一个字符串 `s`,找到 `s` 中最长的回文子串。你可以假设 `s` 的最大长度为 1000。 + +**示例 1:** + +``` +输入: "babad" +输出: "bab" +注意: "aba" 也是一个有效答案。 +``` + +**示例 2:** + +``` +输入: "cbbd" +输出: "bb" +``` + +#### 解题思路 + +就是遍历字符串, + +1.判断每个字符i,是否与上一个字符i-1回文,是的话从i-1向左,i向右,双指针判断,找出最长回文字符串。 + +2.判断每个字符i,它的前一个字符i-1与后一个字符i+1是否回文,是的话,i-1向左,i+1向右,双指针判断,找出最长回文字符串。 + +时间复杂度是O(N^2),空间复杂度是O(1) + +```java +class Solution { + public String longestPalindrome(String s) { + if(s ==null || s.length()<=1) { + return s; + } + char[] array = s.toCharArray(); + String maxString = String.valueOf(array[0]); + for(int i = 1;i=0 && right <= array.length-1 && array[left] == array[right]) { + left--; + right++; + } + //subString是一个左闭右开区间,也就是会包含左边界的值,但是不包含右边界的值,而我们应该要取的值是left+1到right-1。 + return string.substring(left+1,right); + } +} +``` +### 第121题-买卖股票的最佳时机 +题目详情:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/ + +#### 题目介绍 + +给定一个数组,它的第 *i* 个元素是一支给定股票第 *i* 天的价格。 + +如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。 + +注意:你不能在买入股票前卖出股票。 + +**示例 1:** + +``` +输入: [7,1,5,3,6,4] +输出: 5 +解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 + 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。 +``` + +**示例 2:** + +``` +输入: [7,6,4,3,1] +输出: 0 +解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 +``` + +#### 解题思路 + +这个题就是使用min变量记录之前出现的值,使用maxValue保存之前的最大差值,对数组遍历,将当前股票价格prices[i]-min得到利润,如果比maxValue大那么就进行替换,并且如果prices[i]比min变量小,那么也替换出现过的最小值。 + +```java +class Solution { + public int maxProfit(int[] prices) { + if(prices==null||prices.length<=1){ + return 0; + } + int min = prices[0];//之前出现的最小值 + int maxValue = 0;//之前计算得到的最大差值 + for(int i = 1; i < prices.length; i++) { + maxValue = prices[i] - min > maxValue ? prices[i] - min : maxValue; + min = prices[i] < min ? prices[i] : min; + } + return maxValue; + } +} +``` +### 第70题-爬楼梯 +题目详情:https://leetcode-cn.com/problems/climbing-stairs/ + +#### 题目介绍 + +假设你正在爬楼梯。需要 *n* 阶你才能到达楼顶。 + +每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? + +**注意:**给定 *n* 是一个正整数。 + +**示例 1:** + +``` +输入: 2 +输出: 2 +解释: 有两种方法可以爬到楼顶。 +1. 1 阶 + 1 阶 +2. 2 阶 +``` + +**示例 2:** + +``` +输入: 3 +输出: 3 +解释: 有三种方法可以爬到楼顶。 +1. 1 阶 + 1 阶 + 1 阶 +2. 1 阶 + 2 阶 +3. 2 阶 + 1 阶 +``` + +#### 解题思路 + +这个就是斐波拉契数列,就是f(n) = f(n-1)+f(n-2),需要注意的是,如果使用递归来实现,会有重复计算重叠子问题的问题。比如f(5)=f(4)+f(3)=(f(3)+f(2))+(f(2)+f(1)) 其实是计算了两遍f(3),所以可以使用hashMap来缓存f(3)的结果,这样避免重复递归计算。 + +```java +class Solution { + HashMap map = new HashMap(); + public int climbStairs(int n) { + if(n <= 0) {return 0;} + else if(n == 1 || n == 2) { + return n; + } else if (map.containsKey(n)) { + return map.get(n); + } else { + int value = climbStairs(n-1) + climbStairs(n-2); + map.put(n,value); + return value; + } + } +} +``` +### 第53题-最大子序和 +题目详情:https://leetcode-cn.com/problems/maximum-subarray/ + +#### 题目介绍 + +给定一个整数数组 `nums` ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + +**示例:** + +``` +输入: [-2,1,-3,4,-1,2,1,-5,4] +输出: 6 +解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 +``` +#### 解题思路 + +就是遍历一遍,判断包含从0到当前遍历值i的最大子序列和, + +如果sum<0,那么就丢掉之前的子序列,直接让sum=nums[i], + +如果sum>0否则sum=sum+nums[i] + +计算后的sum如果大于maxSum,那么就进行替换。 + + +```java +class Solution { + public int maxSubArray(int[] nums) { + if(nums==null||nums.length==0) { + return 0; + } + if(nums.length==1) { + return nums[0]; + } + int maxSum = nums[0]; + int sum = nums[0]; + for(int i = 1; i < nums.length; i++) { + sum = sum < 0 ? nums[i] : sum + nums[i]; + maxSum = sum > maxSum ? sum : maxSum; + } + return maxSum; + } + +} +``` + + +### 第19题-删除链表的倒数第N个节点 +题目详情:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/ + +#### 题目介绍 + +给定一个链表,删除链表的倒数第 *n* 个节点,并且返回链表的头结点。 + +**示例:** + +``` +给定一个链表: 1->2->3->4->5, 和 n = 2. + +当删除了倒数第二个节点后,链表变为 1->2->3->5. +``` + +**说明:** + +给定的 *n* 保证是有效的。 + +#### 解题思路 + +就是用快慢指针,快指针quickNode先走n步,然后慢指针slowNode从链表头部出发,每次quickNode和slowNode都只走一步,直到快指针quickNode走到最后一步,此时slowNode与quickNode之间相差n步,其实是此时slowNode是倒数第n+1个节点,也就是要删除的节点的前一个节点,直接将slowNode.next = slowNode.next.next;,就可以将节点删除。 + +但是需要考虑到如果删除的是头结点,此时会比较麻烦,严格意义上,m个节点,头结点与最后一个节点之间只存在m-1个节点的间隔,也就是只能走m-1步,所以解决方案就是先建一个临时节点加在头结点前面,这样就可以走出m步了,也就是可以删除倒数第m个节点,也就是头结点了。 + +```java +class Solution { + public ListNode removeNthFromEnd(ListNode head, int n) { + //因为head有可能是要被删除的节点,所以需要建一个preHead方便操作 + ListNode preHead = new ListNode(); + preHead.next = head; + ListNode quickNode = preHead; + while(n>0) { + quickNode = quickNode.next; + n--; + } + ListNode slowNode = preHead;//preDeleteNode就是要删除的节点的前一个节点 + while(quickNode.next!=null) {//这个循环遍历完可以保证quickNode是最后一个节点 + quickNode = quickNode.next; + slowNode = slowNode.next; + } + slowNode.next = slowNode.next.next; + return preHead.next; + } +} +``` + + +### 第21题-合并两个有序链表 +题目详情:https://leetcode-cn.com/problems/merge-two-sorted-lists/ + +#### 题目介绍 + +将两个升序链表合并为一个新的 **升序** 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 + +**示例:** + +``` +输入:1->2->4, 1->3->4 +输出:1->1->2->3->4->4 +``` + +#### 解题思路 + +就是创建一个preNode,作为头结点前面的节点,再创建一个currentNode作为实际遍历时的节点,每次从链表l1和l2各取出节点,进行比较,val较小的节点赋值给currentNode的next指针,然后再将currentNode后移,链表中的节点进行后移。直到某个链表遍历完毕了,然后将另外一个链表后续的节点接到currentNode的next指针上。 + +```java +public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if(l1==null) {return l2;} + if(l2==null) {return l1;} + ListNode preHead = new ListNode(); + ListNode currentNode = preHead; + while(l1!=null && l2!=null) { + if(l1.val < l2.val) { + currentNode.next = l1; + currentNode = currentNode.next; + l1 =l1.next; + } else { + currentNode.next = l2; + currentNode = currentNode.next; + l2 =l2.next; + } + } + if(l1!=null) {currentNode.next = l1;} + if(l2!=null) {currentNode.next = l2;} + return preHead.next; +} +``` + +### 第283题-移动零 + +给定一个数组 `nums`,编写一个函数将所有 `0` 移动到数组的末尾,同时保持非零元素的相对顺序。 + +题目详情:https://leetcode-cn.com/problems/move-zeroes/ + +``` +输入: [0,1,0,3,12] +输出: [1,3,12,0,0] +``` + +解题思路: + +对数组进行遍历,就是找到一个为0的数,然后继续往后找,找到一个不为0的数,与它进行交换,这样就可以把0全部移动到后面去了。 + +```java +public void moveZeroes(int[] nums) { + if(nums==null || nums.length<=1) { return;} + int slow = 0,quick = 0; + while (slow=输入值的元素下标),所以本题可以通过找出target-0.5查找左边界,得到target的最左边的值,同时通过找出target+0.5查找出第一个大于目标值的元素下标,然后-1得到taget最右边的值。(当然也需要考虑taget不存在的情况) + +```java +public int[] searchRange(int[] nums, int target) { + int[] array = new int[2]; + array[0]=-1; + array[1]=-1; + if (nums==null||nums.length==0) { + return array; + } + int left = findLeftBound(nums,target-0.5); + int right = findLeftBound(nums,target+0.5); + if (left==-1) {//nums不存在这个target值 + return array; + } + if (right==-1) {//taget值可能是数组最后一个元素 + right = nums.length-1; + } else {//right是第一个大于target的值,减一得到target的右边界 + right = right -1; + } + //如果相等,那么返回下标 + if (nums[left] == target && nums[right] == target) { + array[0] = left; + array[1] = right; + return array; + } + return array; +} +//查找target值的左边界(也就是第一个>=target的元素下标) +int findLeftBound(int[] nums,double target) { + int left = 0; + int right = nums.length-1; + while (left<=right) { + int mid = left+(right-left)/2; + if (nums[mid] == target) { + right=mid-1; + } else if (nums[mid]>target) { + right = mid-1; + } else if (nums[mid]=nums.length) { + return -1; + } else { + return left; + } +} +``` +### 第11题-盛最多水的容器 + +给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 + +说明:你不能倾斜容器。 +示例 1: + +![img](../static/question_11.jpg)输入:[1,8,6,2,5,4,8,3,7] +输出:49 +解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。 + +##### 解题思路 + +我们取两个指针从数组的两端往内遍历,i从数组头部出发,j从数组尾部出发。每次计算最大面积,并且移动高度较小的那个端点。 + +对于两个端点i和j来说,容纳水的面积是是等于(j-i)*min(height[i],height[j]),假设height[i]是两者之间较小的那一个,那么面积等于(j-i)*height[i],假设i不移动,j向左移动,这样宽度j-i会减少,而height[j]即便变大也不会使得面积变大,因为面积是由宽度乘以两者中较小的高度决定的,所以此时的面积对于i这个端点来说,已经是最大的面积,我们可以右移高度较小的端点i。 + +```java +public int maxArea(int[] height) { + if (height==null||height.length==0) {return 0;} + int left =0; + int right = height.length-1; + int maxArea = 0; + while (left area ? maxArea : area; + left++; + } else { + int area = height[right] * (right-left); + maxArea = maxArea > area ? maxArea : area; + right--; + } + } + return maxArea; + } +``` +### 第17题-电话号码的字母组合 +给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。 +给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 + +示例: + +输入:"23" +输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. + +##### 解题思路: + "" + "a" "b" "c" + "d" "e" "f" "d" "e" "f" "d" "e" "f" + "ad" "ae" "af" "bd" "be" "bf" "cd" "ce" "cf" +其实可以认为这是一个多叉树,根节点到叶子节点的路径就是每一种字符的组合,然后我们可以通过宽度遍历的方式来得到根节点到叶子节点路径。 + + +```java +public List letterCombinations(String digits) { + List list = new ArrayList(); + if (digits==null || digits.length() ==0) { + return list; + } + LinkedList queue = new LinkedList<>(); + // 空字符串是作为多叉树的根节点 + queue.add(""); + //下面是多叉树的宽度遍历的过程 + for (int i = 0; i < digits.length(); i++) { + String[] array = convert(digits.charAt(i)); + //当queue.getFirst().length() == i+1是说明是本次循环添加的值,那么不应该加进来 + while (queue.size()>0&&queue.getFirst().length()<=i) { + String firstValue = queue.removeFirst(); + //拼接后添加到最后面j + for (int j = 0; j < array.length; j++) { + String temp = firstValue+array[j]; + queue.add(temp); + } + } + } + return queue; + } + String[] convert(Character character) { + String[] list = new String[4]; + if (character == '2') { list = new String[]{"a","b","c"}; } + if (character == '3') { list = new String[]{"d","e","f"}; } + if (character == '4') { list = new String[]{"g","h","i"}; } + if (character == '5') { list = new String[]{"j","k","l"}; } + if (character == '6') { list = new String[]{"m","n","o"}; } + if (character == '7') { list = new String[]{"p","q","r","s"}; } + if (character == '8') { list = new String[]{"t","u","v"}; } + if (character == '9') { list = new String[]{"w","x","y","z"}; } + return list; + } +``` + +下面是另外一种解法,主要是多叉树的深度遍历的解法: + +```java +public List letterCombinations(String digits) { + List arrayList = new ArrayList<>(); + if (digits==null||digits.length()==0) { + return arrayList; + } + tranverse(digits,"",0,arrayList); + return arrayList; +} +//递归进行深度遍历 +public void tranverse(String digits,String currentString,int start,List arrayList) { + //下一层已经没有节点时,直接添加字符串 + if (start>=digits.length()) { + arrayList.add(currentString); + return; + } + //convert方法就不重复列出来了,在上面的解法中有相关的实现 + String[] array = convert(digits.charAt(start)); + //对每一个子节点进行继续递归遍历 + for (int i = 0; i < array.length; i++) { + String temp = currentString + array[i]; + tranverse(digits,temp,start+1,arrayList); + } +} +``` + +### 第15题-三数之和 + +给你一个包含 *n* 个整数的数组 `nums`,判断 `nums` 中是否存在三个元素 *a,b,c ,*使得 *a + b + c =* 0 ?请你找出所有满足条件且不重复的三元组。 + +**注意:**答案中不可以包含重复的三元组。 + +**示例:** + +```java +给定数组 nums = [-1, 0, 1, 2, -1, -4], + +满足要求的三元组集合为: +[ + [-1, 0, 1], + [-1, -1, 2] +] +``` + +##### 解题思路 +两数之和的是通过两个指针从首尾两端向中间移动来解决的,三数之和相当于是基于两数之和来解答的,相当于对数组进行遍历,对于每个数调用两数之和的函数获得结果,大致思路如下: +```java +for(int i=0;i> totalList = new ArrayList<>(); + public List> threeSum(int[] nums) { + //先对数组进行排序 + Arrays.sort(nums); + for (int i = 0; i < nums.length; i++) { + //说明上次循环已经添加了 + if(i>=1 && nums[i] == nums[i-1]) { + continue; + } + int target = 0 - nums[i]; + //这里不是从0开始遍历,而是只是对后面的元素遍历,防止三元组重复 + twoSum(nums, i+1, target); + } + return totalList; + } + + public void twoSum(int[] nums,int start, int target) { + int i = start,j=nums.length-1; + while (istart && nums[i] == nums[i-1]){ + i++; + continue; + } else if (sum == target) { + List list = new ArrayList<>(); + list.add(0-target); + list.add(nums[i]); + list.add(nums[j]); + totalList.add(list); + i++; + j--; + } else if (sum > target) { + j--; + } else if(sum rightDepth ? leftDepth+ 1: rightDepth+1; + } +``` + +## 第22题-括号生成 +数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 + +示例: + +输入:n = 3 +输出:[ + "((()))", 000111 + "(()())", 001011 + "(())()", 001101 + "()(())", 010011 + "()()()" 010101 + ] + +##### 解题思路 + +其实这一题也是一个回朔的解法,就是可以把每个"("和")"看成是一个二叉树的节点,从根节点到叶子节点的路径组成了每种括号组合。例如n=3时,二叉树如下: + +二叉树第1层 "" + +第2层 ( ) + +​ ( ) ( ) + +( ) ( ) ( ) ( ) + +每次就使用回朔的方法进行深度遍历,每当发现当前的路径不符合要求时,进行剪枝,回退到上一层,遍历其他路径。(本题中剪枝的要求是左括号数量>n,或者右括号数量>n,或者右括号数量>左括号。) + +```java + public List generateParenthesis(int n) { + List totalList = new ArrayList(); + if(n<=0) { + return totalList; + } + LinkedList stack = new LinkedList(); + //使用回朔算法进行遍历 + generateParenthesis(n,0,0,stack,totalList); + return totalList; + } + + public void generateParenthesis(int n,int left,int right,LinkedList stack,List totalList) { + //不满足要求,进行剪枝,回退去遍历其他节点 + if(left>n || right>n || right>left) {return;} + if(left==n&&right==n) {//正好匹配上了,将栈中所有值转换为 + StringBuffer str = new StringBuffer(); + for(int i = 0;i < stack.size(); i++) { + str.append(stack.get(i)); + } + totalList.add(str.toString()); + } + //往左边遍历 + stack.add('('); + generateParenthesis(n,left+1,right,stack,totalList); + //回朔 + stack.removeLast(); + //往右边遍历 + stack.add(')'); + generateParenthesis(n,left,right+1,stack,totalList); + //回朔 + stack.removeLast(); + } +``` + +PS:回朔算法的框架 + +``` +List result = new ArrayList<>(); +void backtrack(路径, 选择列表stack): + if 超出范围 return + + if 满足结束条件: + result.add(路径) + return + + for 选择 in 选择列表: + 做选择 stack.add(当前元素); + backtrack(路径, 选择列表) + 撤销选择 stack.remove(当前元素); +``` +### 第42题-接雨水 + +给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 + +示例 1: + +![img](../static/rainwatertrap.png) + +输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] +输出:6 +解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 + +##### 解题思路 + +就是对于每个柱子来说,它这个位置顶上可以容纳的水,其实是等于柱子左边柱子的最大值leftMax,右边柱子的最大值rightMax,两者中的较小值min,水容量=min-height[i]。所以先统计出每个柱子左边的最大值和右边的最大值,然后就可以计算水容量了。 + +```java +public int trap(int[] height) { + if (height==null||height.length==0){return 0;} + int[][] dp = new int[height.length][2]; + int leftMax = height[0]; + //统计每个柱子左边的最大值 + for (int i = 1; i < height.length; i++) { + leftMax = leftMax > height[i] ? leftMax : height[i]; + dp[i][0] = leftMax; + } + int rightMax = height[height.length-1]; + //统计每个柱子右边的最大值 + for (int i = height.length-2; i >=0 ; i--) { + rightMax = rightMax > height[i] ? rightMax : height[i]; + dp[i][1] = rightMax; + } + int area = 0; + //统计每个柱子可以存的水 + for (int i = 1; i <= height.length-2; i++) { + int min = dp[i][0] < dp[i][1] ? dp[i][0] : dp[i][1]; + area += min-height[i]; + } + return area; + } +``` + + + + +### 第102题-二叉树的层序遍历 + +给你一个二叉树,请你返回其按 **层序遍历** 得到的节点值。 (即逐层地,从左到右访问所有节点)。 + +示例: + +``` +二叉树:[3,9,20,null,null,15,7], + + 3 + / \ + 9 20 + / \ + 15 7 +返回其层次遍历结果: + +[ + [3], + [9,20], + [15,7] +] +``` + +##### 解题思路 + +其实就是二叉树的宽度优先遍历,只不过返回结果,是每一层的节点存在同一个数组中 + +```java +public List> levelOrder(TreeNode root) { + List totalList = new ArrayList<>(); + if(root==null){ + return totalList; + } + LinkedList currentQueue = new LinkedList(); + LinkedList nextQueue = new LinkedList(); + currentQueue.add(root); + while(currentQueue.size()>0) { + List list = new ArrayList(); + while(currentQueue.size()>0) { + TreeNode node = currentQueue.removeFirst(); + if(node != null) { + list.add(node.val); + if(node.left!=null) { + nextQueue.add(node.left); + } + if(node.right!=null) { + nextQueue.add(node.right); + } + } + } + totalList.add(list); + currentQueue = nextQueue; + nextQueue = new LinkedList(); + } + return totalList; + } +``` + + + +### 第198题-打家劫舍 +你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 + +给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 + +示例 1: + +输入:[1,2,3,1] +输出:4 +解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 +  偷窃到的最高金额 = 1 + 3 = 4 。 + +##### 解题思路: + +其实跟斐波拉契数列很像,这道题中其实如果要计算nums数组的前n个元素的最高金额的话,使用f(n)来代替。 + +f(0)=nums[0]; + +f(1)=nums[1]>nums[0]?nums[1]:nums[0];//也就是num[0]和nums[1]之间的最大值。 + +状态转移方程如下: + +f(n) = f(n-1) > f(n-2)+nums[n] ? f(n-1) :f(n-2)+nums[n] + +```java +Integer[] saveTable; + public int rob(int[] nums) { + if(nums==null|| nums.length == 0) { + return 0; + } + if(nums.length==1) { + return nums[0]; + } + saveTable = new Integer[nums.length]; + int value1 = maxRob(nums,nums.length-1); + int value2 = maxRob(nums,nums.length-2); + return value1 > value2 ? value1 : value2; + } + + public int maxRob(int[] nums,int n) { + if(saveTable[n]!=null) + { + return saveTable[n]; + } + int max = 0; + if(n==0) { + max = nums[0]; + } else if(n==1) { + max = nums[1]>nums[0]?nums[1]:nums[0]; + } else if(n-2>=0) { + int value1 = maxRob(nums,n-1); + int value2 = maxRob(nums,n-2)+nums[n]; + max = value1 > value2 ? value1 : value2; + } + saveTable[n] = max; + return max; + } +``` + +### 第46题-全排列 + +给定一个 **没有重复** 数字的序列,返回其所有可能的全排列。 +**示例:** + +``` +输入: [1,2,3] +输出: +[ + [1,2,3], + [1,3,2], + [2,1,3], + [2,3,1], + [3,1,2], + [3,2,1] +] +``` + +##### 递归解法 + +假设你需要计算[1,2,3]的全排列结果,其实等于 + +1为首元素,[2,3]的全排列结果, + +2为首元素,[1,3]的全排列结果, + +3首元素,[1,2]的全排列结果 + +```java + public List> permute(int[] nums) { + List> totalList = new ArrayList>(); + if (nums == null|| nums.length==0){ + return totalList; + } + tranverse(nums,0,totalList); + return totalList; + } + //遍历 + public void tranverse(int[] nums,int start,List> totalList) { + if (start>=nums.length) {//说明递归到最后了,将所有元素添加到list + List list = new ArrayList(); + for (int i = 0; i < nums.length; i++) { + list.add(nums[i]); + } + totalList.add(list); + return; + } + //遍历将后面的元素取出,与首元素交换,然后对子串递归,因为对于[1,2,3]而言,所有排序结果是等于1为首元素,后面子数组的结果+2为首元素,后面子数组的结果+3为首元素,后面子数组的结果 + for (int i = start; i < nums.length; i++) { + swap(nums,start,i); + tranverse(nums,start+1,totalList); + swap(nums,i,start); + } + } + void swap(int[] nums,int a,int b) { + int temp = nums[a]; + nums[a] = nums[b]; + nums[b] = temp; + } +``` + +##### 回朔解法 + +回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优[搜索](https://baike.baidu.com/item/搜索/2791632)法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。这道题中其实就是用回朔方法对多叉树进行一次遍历,每次到叶子节点后,发现没有元素了就退回。 + +![6111](../static/6111.jpeg) + +```java +List> totalList = new ArrayList>(); +LinkedList stack = new LinkedList(); +HashSet set = new HashSet(); +public List> permute1(int[] nums) { + if (nums==null||nums.length==0) { + return totalList; + } + permute1(nums); + return totalList; +} +public void permute1(int[] nums) { + if (stack.size()==nums.length) {//排列完毕了 + LinkedList newList = new LinkedList<>(stack); + totalList.add(newList); + return; + } + for (int i = 0; i < nums.length; i++) { + if (set.contains(nums[i])) {//包含说明此元素在前面出现过了 + continue; + } + //在剩余元素中找到一个stack中未出现的,然后添加到stack + stack.add(nums[i]); + set.add(nums[i]); + permute1(nums,stack,totalList); + //回撤 + stack.removeLast(); + set.remove(nums[i]); + } +} +``` + +### 第55题-跳跃游戏 + +给定一个非负整数数组,你最初位于数组的第一个位置。 + +数组中的每个元素代表你在该位置可以跳跃的最大长度。 + +判断你是否能够到达最后一个位置。 + +示例 1: + +输入: [2,3,1,1,4] +输出: true +解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。 + +##### 解题思路 + +这个其实也是通过回朔法去判断每个下标能否到达最后一步 + +```java + //这个数组主要用于记录那些不能到达最后一个元素的数组下标,减少冗余计算 + Boolean[] recordArray; + public boolean canJump(int[] nums) { + if(nums==null||nums.length==0) {return false;} + recordArray = new Boolean[nums.length]; + return canJump(nums,0); + } + + public boolean canJump(int[] nums,int start) { + //当前start已经处于最后一步了,或者是当前数组下标加上数字超过最后一个元素了 + if(start >= nums.length-1 || start+nums[start] >= nums.length-1) { + return true; + } + //已经对于改已经有记录结果,不用重复计算 + if(recordArray[start]!=null) { + return recordArray[start]; + } + int end = start+nums[start]; + //计算[start+1,end]之间的元素,是否有可以到达最后一步的 + for(int i = start+1;i<=end;i++) { + if(canJump(nums,i)) { + return true; + } + } + recordArray[start] = false; + return false; + } +``` + +##### 贪心解法 + +```java +public boolean canJump(int[] nums) { + if(nums==null||nums.length==0) {return false;} + //maxDepth代表可以抵达的最远距离 + int maxDepth = nums[0]; + for(int i=1;i<=maxDepth && i< nums.length;i++){ + //当前元素的可抵达距离超过maxDepth,进行更新 + if(nums[i]+i>maxDepth){ + maxDepth = nums[i]+i; + } + } + return maxDepth>= nums.length-1; + } +``` + +### 第62题-不同路径 + +一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 + +机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 + +问总共有多少条不同的路径? 右6 下2 C 2 8 + + + +![img](../static/robot_maze.png) + +输入:m = 3, n = 2 +输出:3 +解释: +从左上角开始,总共有 3 条路径可以到达右下角。 +1. 向右 -> 向右 -> 向下 +2. 向右 -> 向下 -> 向右 +3. 向下 -> 向右 -> 向右 + +##### 解题思路 + +其实就是C(m-1,m-1+n-1),其实总共要走m-1+n-1步,其中有m-1步是向右的,n-1步是向下的,所以其实是一个组合问题,相当于在m-1+n-1步中找出m-1步的组合数。 + +```java +public int uniquePaths(int m, int n) { + int rightStep = m-1; + int downStep = n-1; + int min = rightStep0) { + allTimes = allTimes*sum; + innerTimes = innerTimes *min; + min--; + sum--; + } + return (int) (allTimes/innerTimes); + } +``` +### 第56题-合并区间 + +给出一个区间的集合,请合并所有重叠的区间。 + +示例 1: +``` +输入: intervals = [[1,3],[2,6],[8,10],[15,18]] +输出: [[1,6],[8,10],[15,18]] +解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]。 +``` + +##### 解题思路 + +就是先根据左边界进行排序,排序完之后进行进行区间合并,合并的判断规则就是当前区间的左边界是否在上一个区间内。 + +```java +public int[][] merge(int[][] intervals) { + if (intervals==null||intervals.length<=1) { + return intervals; + } + //先进行排序 + quickSort(intervals,0,intervals.length-1); + int lastIndex = 0; + for (int i = 1; i < intervals.length; i++) { + if (intervals[i][0] >= intervals[lastIndex][0] + && intervals[i][0] <= intervals[lastIndex][1]) { + //如果当前区间的左边界处于上一个区间中间,说明可以被合并 + intervals[lastIndex][1] = intervals[lastIndex][1] > intervals[i][1] + ? intervals[lastIndex][1] : intervals[i][1]; + } else {//不能被合并 + lastIndex++; + intervals[lastIndex][0] = intervals[i][0]; + intervals[lastIndex][1] = intervals[i][1]; + } + } + //对数组进拷贝 + int[][] result = new int[lastIndex+1][2]; + for (int i = 0; i <= lastIndex ; i++) { + result[i][0] = intervals[i][0]; + result[i][1] = intervals[i][1]; + } + return result; + } + //快排,按照每个区间的左边界进行排序 + void quickSort(int[][] array,int start,int end) { + if (start>=end){return;} + int i = start; + int j = end; + int base = array[start][0]; + while (i base && j>i) {j--;} + while (array[i][0]<=base&&j>i) {i++;} + swap(array,i,j); + } + swap(array,start, i); ; + quickSort(array,start,i-1); + quickSort(array,i+1,end); + } + //交换元素 + void swap(int[][] array, int i,int j) { + int temp_0 = array[j][0]; + int temp_1 = array[j][1]; + array[j][0] = array[i][0]; + array[j][1] = array[i][1]; + array[i][0] = temp_0; + array[i][1] = temp_1; + } +``` +### 第169题-多数元素 +给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于n/2 的元素。 + +你可以假设数组是非空的,并且给定的数组总是存在多数元素。 + +示例 1: +``` +输入: [3,2,3] +输出: 3 +``` +##### 解题思路 +这个多数元素一定是数组的中位数,所以可以转换为寻找数组的中位数,也就是寻找第nums.length/2小的元素,也就转换为Top K问题了,所以使用快排解决。 +```java +public int majorityElement(int[] nums) { + if (nums==null||nums.length==0) { return 0; } + return quickSort(nums,nums.length/2,0,nums.length-1); + } + int quickSort(int[] nums,int k,int start,int end) { + if (start>=end) { + return nums[start]; + } + int base = nums[start]; + int i = start; + int j = end; + while (ibase&&ik) { + return quickSort(nums,k,start,i-1); + } else { + return quickSort(nums,k,i+1,end); + } + } +``` +### 第101题-对称二叉树 + +给定一个二叉树,检查它是否是镜像对称的。 + +例如,二叉树 [1,2,2,3,4,4,3] 是对称的。 + + 1 + / \ + 2 2 + / \ / \ + 3 4 4 3 +##### 解题思路 + +```java +public boolean isSymmetric(TreeNode root) { + if (root == null) { + return true; + } + return isSymmetric(root.left, root.right); +} +public boolean isSymmetric(TreeNode left, TreeNode right) { + if (left == null && right == null) {//都为null + return true; + } + if ((left == null && right != null) || (left != null && right == null)) {//其中一个为null + return false; + } + if (left.val != right.val) {//都不为null但是值不相等 + return false; + } + //判断子节点是否相等 + return isSymmetric(left.left, right.right) && isSymmetric(left.right, right.left); +} +``` +### 第33题-搜索旋转排序数组 +升序排列的整数数组 nums 在预先未知的某个点上进行了旋转(例如, [0,1,2,4,5,6,7] 经旋转后可能变为 [4,5,6,7,0,1,2] )。 + +请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 +示例 1: + +输入:nums = [4,5,6,7,0,1,2], target = 0 +输出:4 +##### 解题思路 +还是按照二分搜索来进行搜索,只是多一步判断,如果nums[mid]nums[mid] && target<=nums[right]) { + left = mid+1; + } else { + right = mid-1; + } + } else {//说明旋转点在右边,这是我们根据左边来判断 + if(target>=nums[left] && target < nums[mid]) { + right = mid-1; + } else { + left = mid+1; + } + } + } + return -1; + } +``` +### 第136题-只出现一次的数字 + +给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 + +说明: + +你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? + +示例 1: + +输入: [2,2,1] +输出: 1 + +##### 解题思路 + +```java +public int singleNumber(int[] nums) { + int value =0; + for(int i = 0;i4->5, + 1->3->4, + 2->6 +] +将它们合并到一个有序链表中得到。 +1->1->2->3->4->4->5->6 + +##### 解题思路 +就是对K个链表的头结点建立一个小顶堆,每次取堆顶元素出来,放到新链表的末尾。然后对堆进行调整,每次调整复杂度为logK,总时间复杂度是N*LogK. +```java +public ListNode mergeKLists(ListNode[] lists) { + if(lists==null||lists.length==0) {return null;} + ListNode preHead = new ListNode(-1); + ListNode currentNode = preHead; + ArrayList arrayList = new ArrayList<>(); + //过滤lists中为null的元素,然后将不为null的元素添加到arrayList中去 + // (测试用例中有很多为null的用例) + for (int i = 0; i < lists.length; i++) { + if (lists[i] != null) { + arrayList.add(lists[i]); + } + } + if (arrayList.size()==0) { + return null; + } + //建立小顶堆 + for (int i = arrayList.size()/2-1; i >= 0; i--) { + adjustHeap(arrayList,i,arrayList.size()); + } + while (arrayList.size()>0) { + if (arrayList.get(0) == null) {//这个链表到最后一个节点了,从小顶堆中移除 + swap(arrayList,0,arrayList.size()-1); + arrayList.remove(arrayList.size()-1); + continue; + } + adjustHeap(arrayList,0,arrayList.size()); + + ListNode node = arrayList.get(0); + currentNode.next = node; + currentNode = currentNode.next; + arrayList.set(0,node.next); + } + return preHead.next; + } + + void adjustHeap(ArrayList lists, int i, int length) { + while (2*i+1 lists,int a, int b) { + ListNode temp = lists.get(a); + lists.set(a,lists.get(b)); + lists.set(b,temp); + } +``` + +### 第94题-二叉树的中序遍历 + +给定一个二叉树的根节点 `root` ,返回它的 **中序** 遍历。 + +**示例 1:** + +![img](../static/inorder_1.jpg) + +``` +输入:root = [1,null,2,3] +输出:[1,3,2] +``` + +##### 解题思路 + +递归解法 + +```java +List list = new ArrayList(); +public List inorderTraversal(TreeNode root) { + if(root==null){return list;} + inorderTraversal(root.left); + list.add(root.val); + inorderTraversal(root.right); + return list; +} +``` + +栈解法 + +就是遍历到每个节点时,先把这个节点的右节点right添加到栈,再把这个节点添加到栈,再把这个节点的左节点left添加到栈。再把这个节点的left和right指针设置为null,代表这个节点的左右子节点已经被访问到了,下次遍历到这个节点时可以直接添加到列表中。 + +```java +public List inorderTraversal1(TreeNode root) { + List list = new ArrayList(); + if (root==null){return list;} + Stack stack = new Stack<>(); + stack.add(root); + while (stack.size()>0) { + TreeNode node = stack.pop(); + if (node.left == null && node.right==null) { + list.add(node.val); + continue; + } + if (node.right!=null) { + stack.push(node.right); + //这里将right置为null主要防止父节点多次遍历,也可以使用一个HashSet来记录那些已经添加子节点的父节点。 + node.right =null; + } + stack.push(node); + if (node.left!=null) { + stack.push(node.left); + //这里将left置为null主要防止父节点多次遍历,也可以使用一个HashSet来记录那些已经添加子节点的父节点。 + node.left=null; + } + } + return list; +} +``` + +### 第64题-最小路径和 + +给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + +说明:每次只能向下或者向右移动一步。 + +示例 1: + +![img](../static/minpath.jpg) + +``` +输入:grid = [[1,3,1],[1,5,1],[4,2,1]] +输出:7 +解释:因为路径 1→3→1→1→1 的总和最小。 +``` + +##### 解题思路 + +其实跟斐波拉契数列的题很相似 + +```java +int[][] cache; +public int minPathSum(int[][] grid) { + if (grid==null||grid[0]==null) {return 0;} + cache = new int[grid.length][grid[0].length]; + return minPathSum(grid,0,0); +} + +public int minPathSum(int[][] grid, int row,int col) { + //缓存中已有这一步到右下角的数据,直接从缓存中取值 + if (cache[row][col]!=0) { + return cache[row][col]; + } + int max_row = grid.length-1; + int max_col = grid[0].length-1; + int path = 0; + if (row == max_row && col == max_col) {//已经处于右下角 + path = grid[row][col]; + } else if (row == max_row) {//当前处于最下面的一行,只能往右边走 + path = grid[row][col] + minPathSum(grid,row,col+1); + } else if (col == max_col) {//当前处于最右边的一行,只能往下边走 + path = grid[row][col] + minPathSum(grid,row+1,col); + } else { + //往下走 + int down = grid[row][col] + minPathSum(grid, row, col + 1); + int right = grid[row][col] + minPathSum(grid, row + 1, col); + path = down < right ? down : right; + } + cache[row][col] = path; + return path; +} +``` +### 第215题-数组中的第K个最大元素 + +在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 + +示例 1: + +输入: [3,2,1,5,6,4] 和 k = 2 +输出: 5 + +##### 解题思路 + +就是Top K问题,这里可以使用堆排进行排序,需要注意的是,大顶堆建堆结束后,每次将堆顶元素移动到数组末尾,然后继续对剩下的0到i-1范围内的元素进行调整,所以是adjustHeap(nums,0,i); +```java +public int findKthLargest(int[] nums, int k) { + for (int i = nums.length/2-1; i >=0 ; i--) { + //对整个数组看成一个对,进行大顶堆调整 + adjustHeap(nums,i,nums.length); + } + for (int i = nums.length-1; i > 0; i--) { + swap(nums,0,i); + //对0到i-1范围内的元素看成一个堆,进行大顶堆调整 + if (i==nums.length-k) { + break; + } + adjustHeap(nums,0,i); + } + return nums[nums.length - k]; + } + void adjustHeap(int[] nums, int i,int currentLength) { + //左子节点存在 + while (2*i+1 nums[left]) {//右节点也存在,并且大于左节点 + if (nums[right] > nums[i]) {//右节点大 + swap(nums,i,right); + i = right; + } else {//根节点最大 + break; + } + } else {//右节点不存在,或者右节点比左节点小 + if (nums[left] > nums[i]) { + swap(nums,i,left); + i = left; + } else { + break; + } + } + } + } +``` + +### 第234题-回文链表 +请判断一个链表是否为回文链表。 + +示例 1: + +输入: 1->2 +输出: false +示例 2: + +输入: 1->2->2->1 +输出: true +##### 解题思路 +就是用一个快慢指针,找到链表的中位数节点,然后对后半部分链表进行反转,然后分别从原链表头部和尾部开始遍历判断。 +```java + public boolean isPalindrome(ListNode head) { + ListNode slow = head; + ListNode quick = head; + while (quick != null) { + quick = quick.next; + if (quick == null) { + break; + } + quick = quick.next; + slow = slow.next; + } + //此时的slow要么是中位数节点, + // 中位数有两个时,就是靠前的那个中位数节点 + //总结点数为奇数 1->2->3->2->1 slow为3 + //总结点数为偶数数 1->2->2->1 slow为第一个2 + //对后面的节点进行翻转 + ListNode preNode = slow; + ListNode currentNode = slow.next; + while (currentNode!=null) { + ListNode tempNode = currentNode.next; + currentNode.next = preNode; + preNode = currentNode; + currentNode = tempNode; + } + slow.next = null; + ListNode otherHead = preNode; + while (head!=null && otherHead!=null) { + if ((head==null&&otherHead!=null) + || (head!=null&&otherHead==null)) { + return false; + } + if (head.val!=otherHead.val) { + return false; + } + head=head.next; + otherHead = otherHead.next; + } + return true; + } +``` + +### 第200题-岛屿数量 +给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 + +岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 +此外,你可以假设该网格的四条边均被水包围。 +示例 1: + +输入:grid = [ + ["1","1","1","1","0"], + ["1","1","0","1","0"], + ["1","1","0","0","0"], + ["0","0","0","0","0"] +] +输出:1 + +#### 解题思路 +因为每个岛屿中所有的1之间是可以相互抵达的,只要你找到岛屿中的一个1,然后向四周进行遍历,判断是1继续向四周扩散,可以抵达这个岛屿所有的节点,所以通过infect函数对每个未被遍历的岛屿点进行扩散,判断这个点是1就将它设置为2,代表已遍历,然后继续扩散。这样就可以统计出岛屿数量。 +```java +public int numIslands(char[][] grid) { + if (grid==null||grid[0]==null){return 0;} + int num =0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + char c = grid[i][j]; + if (c == '1') {//如果是未感染的陆地,那么使用infect函数向四周扩散 + infect(grid,i,j); + num++; + } + } + } + return num; + } + //向四周未被遍历的陆地进行扩散 + void infect(char[][] grid,int i,int j) { + if (i < 0 || i >= grid.length || j < 0 || j >= grid[i].length) { + return; + } + if (grid[i][j] == '1') { + grid[i][j]= '2'; + infect(grid,i-1,j); + infect(grid,i+1,j); + infect(grid,i,j-1); + infect(grid,i,j+1); + } + } +``` +### 第48题-旋转图像 + +给定一个 n × n 的二维矩阵表示一个图像。 +将图像顺时针旋转 90 度。 +说明: +你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。 +示例 1: +给定 matrix = +[ + [1,2,3], + [4,5,6], + [7,8,9] +], + +原地旋转输入矩阵,使其变为: +[ + [7,4,1], + [8,5,2], + [9,6,3] +] +##### 解题思路 +这个题就是你可以每次只旋转外圈元素,然后旋转完毕后把内圈元素看成一个新的矩阵,继续对矩阵进行旋转。在对外圈元素进行旋转时,我们只需要先将最上方一条边的元素与右方元素交换,再与下方元交换,在于左方元素交换,这样最好就旋转成功了。 + +例如: + [1,2,3], + [4,5,6], + [7,8,9] + 对于这个矩阵来说,我们只旋转外圈元素,也就是1,2,3,6,9,8,7,4,1。然后把里面的元素,也就是5看成一个新的矩阵,继续对外圈元素进行旋转,直到最后矩阵只剩下一个元素。 + +如何对外圈元素进行旋转呢? +我们对最上面的一条边进行遍历,也就是[1,3)进行遍历,例如一开始的元素是1,将左上角的元素1与右上角的元素3交换,此时左上角元素为3,再将左上角的3与右下角的9交换,此时左上角为9,再将左上角的9与左下角的7进行交换,这样对于四个角的元素来说,就完成了旋转,然后继续遍历,对2,6,8,4四个元素按照相同的方法进行旋转。 +```java +public void rotate(int[][] matrix) { + if (matrix == null || matrix[0] == null) { + return; + } + rotate(matrix,0,matrix.length-1,0,matrix[0].length-1); + } + //对矩阵进行旋转 + public void rotate(int[][] matrix,int rowStart,int rowEnd,int colStart,int colEnd) { + if (rowStart>=rowEnd || colStart>= colEnd) { + return; + } + //进行交换 + for (int j = colStart; j < colEnd; j++) { + //当前j从原点走了几步 + int race = j-colStart; + //左上角与右上角元素交换 + swap(matrix,rowStart,j,rowStart+race,colEnd); + //左上角与右下角元素交换 + swap(matrix,rowStart,j,rowEnd,colEnd-race); + //左上角与左下角元素交换 + swap(matrix,rowStart,j,rowEnd-race,colStart); + } + //然后对内圈元素进行旋转 + rotate(matrix,rowStart+1,rowEnd-1,colStart+1,colEnd-1); + } + + void swap(int[][] matrix,int i,int j,int other_i,int other_j) { + int temp = matrix[i][j]; + matrix[i][j] = matrix[other_i][other_j]; + matrix[other_i][other_j] = temp; + } +``` + +### 第98题-验证二叉搜索树 + +给定一个二叉树,判断其是否是一个有效的二叉搜索树。 + +假设一个二叉搜索树具有如下特征: + +节点的左子树只包含小于当前节点的数。 +节点的右子树只包含大于当前节点的数。 +所有左子树和右子树自身必须也是二叉搜索树。 +示例 1: + +输入: + 2 + / \ + 1 3 +输出: true + +##### 解题思路 + +二叉搜索树的中序遍历结果就是一个排序好的序列,所以我们可以对二叉树进行中序遍历 + +,判断当前的遍历节点值是否大于上一个节点值。 + +```java +Integer lastValue = null; +public boolean isValidBST(TreeNode root) { + if (root==null) {return true;} + Boolean leftResult = isValidBST(root.left); + if (leftResult==false){return false;} + if (lastValue==null){ + lastValue=root.val; + } else if (lastValue>=root.val) { + return false; + } else if (lastValue> subsets(int[] nums) { + List> totalList = new ArrayList>(); + int size = (int)Math.pow(2,nums.length); + for (int i = 0; i < size; i++) { + List list = new ArrayList<>(); + for (int j = 0; j < nums.length; j++) { + //j是元素在数组中的位置,通过判断i的第j个二进制位是否为0,来决定是否添加这个元素 + int result = i & (1<> totalList = new ArrayList>(); + public List> combinationSum(int[] candidates, int target) { + combinationSum(candidates,target,candidates.length-1,new LinkedList()); + return totalList; + } + + public void combinationSum(int[] candidates, + int target, int end, + LinkedList stack) { + if (target==0) { + List copyList = new ArrayList<>(stack); + totalList.add(copyList); + return; + } else if (target<0||end<0) { + return; + } + + for (int i = 0; i*candidates[end] <= target ; i++) { + for (int j = 0; j < i; j++) {//添加j个当前元素,也就是假设子集和中有j个candidates[end]元素 + stack.add(candidates[end]); + } + combinationSum(candidates,target-i*candidates[end],end-1,stack); + for (int j = 0; j < i; j++) {//移除j个当前元素 + stack.removeLast(); + } + } + } +``` + +### 第226题-翻转二叉树 + +翻转一棵二叉树。 + +示例: + + 输入: + 4 + / \ + 2 7 + / \ / \ + 1 3 6 9 + 输出: + 4 + / \ + 7 2 + / \ / \ + 9 6 3 1 + +##### 解题思路 + +```java +public TreeNode invertTree(TreeNode root) { + if (root==null) {return null;} + TreeNode temp = root.left; + root.left = root.right; + root.right = temp; + invertTree(root.left); + invertTree(root.right); + return root; +} +``` + +## 第31题-下一个排列 + +实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。 + +如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 + +必须 原地 修改,只允许使用额外常数空间。  + +示例 1: + +输入:nums = [1,2,3] +输出:[1,3,2] + +##### 解题思路 +本题其实就是提升数字的字典序,并且要提升的幅度最小。就是从后面往前找到第一个nums[i-1]0 ; i--) { + + if (nums[i-1] nums[i-1]) { + min = j; + } + } + //进行交换 + int temp = nums[i-1]; + nums[i-1] = nums[min]; + nums[min] = temp; + flag=1; + //然后再对后面的元素进行排序 + Arrays.sort(nums,i,nums.length); + break; + } + } + //如果现在的数组就是字典序最大的,那么就排序,得到字典序最小的进行返回。 + if (flag==0) { + Arrays.sort(nums); + } + } +``` + + +### 第322题-零钱兑换 + +题目详情:(https://leetcode-cn.com/problems/coin-change/) +给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 + +你可以认为每种硬币的数量是无限的。 + +示例 1: + +输入:coins = [1, 2, 5], amount = 11 +输出:3 +解释:11 = 5 + 5 + 1 + +##### 递归解法 + +```java + Integer[] array; + public int coinChange(int[] coins, int amount) { + if (array == null) {//初始化缓存数组 + array = new Integer[amount+1]; + } + if (amount == 0) { + return 0; + } else if (amount < 0) { + return -1; + } else if(array[amount]!=null) {//缓存数组中有值,直接返回 + return array[amount]; + } + Integer minNum = null; + for (int i = 0; i < coins.length; i++) {//遍历计算最大值 + int remainNum = coinChange(coins, amount - coins[i]); + if (remainNum == -1) { + continue; + } else if(minNum==null || remainNum + 1 < minNum ) { + minNum = remainNum + 1; + } + } + array[amount] = minNum; + return minNum == null ? -1 : minNum; + } +``` + +##### 动态规划解法 + +1.找到Base Case + +金额为0时,需要返回的硬币数是0。f(0)=0 + +2.确定状态 + +也就是原问题和子问题中会变化的变量。这个问题中的变量就是金额会变化,硬币面额是确定的,数量是无限。 + +3.确定选择 + +也就是导致「状态」产生变化的行为,这个题里面的选择就是在凑金额的时候,硬币面额的选择, + +4.确定对应的`dp`函数/数组 + +这里会有一个递归的 `dp` 函数,一般来说函数的参数就是状态转移中会变化的量,也就是上面说到的「状态」;函数的返回值就是题目要求我们计算的量。就这个题来说,状态只有一个,即「目标金额」,题目要求我们计算凑出目标金额所需的最少硬币数量,每个目标金额的最少硬币数量=min(目标金额-硬币面额后的金额所需最少硬币数),所以我们可以这样定义 `dp` 函数: + +![coin](../static/coin.png) + +搞清楚上面这几个关键点,解法的伪码就可以写出来了: + +```python +# 伪码框架 +def coinChange(coins: List[int], amount: int): + # 定义:要凑出金额 n,至少要 dp(n) 个硬币 + def dp(n): + # 做选择,选择需要硬币最少的那个结果 + for coin in coins: + res = min(res, 1 + dp(n - coin)) + return res + # 题目要求的最终结果是 dp(amount) + return dp(amount) +``` + +```java +//动态规划解法 +public int coinChange1(int[] coins, int amount) { + int[] array = new int[amount+1]; + for (int i = 1; i <= amount; i++) { + Integer minNum=null; + for (int j = 0; j < coins.length; j++) { + int remain = i - coins[j]; + if (remain < 0 || array[remain]==-1) { + continue; + } + //此时肯定有值 + if (minNum==null || array[remain]+1 < minNum ) { + minNum = array[remain]+1; + } + } + array[i] = minNum == null ? -1 : minNum; + } + return array[amount]; +} +``` + +### 第300题-最长递增子序列 + +给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 + +子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 + + +示例 1: + +输入:nums = [10,9,2,5,3,7,101,18] +输出:4 +解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。 + +##### 解题思路 +使用一个数组dp[i]来记录包含元素i时,最大递增子序列长度,所以状态转移方程为 +dp[i] = max(1,dp[k]+1); k=0; j--) { + if (nums[j] < nums[i] && dp[j] + 1 > max) { + max = dp[j] + 1; + } + } + dp[i] = max; + totalMax = max > totalMax ? max : totalMax; + } + return totalMax; + } + +``` + +时间复杂度为O(NlogN)的解法,就是使用一个数组sizeArray记录元素对应的最长子序列长度,sizeArray[i]代表sizeArray[i]这个元素组成的最长子序列的长度为i+1 + +```java +public int anotherLengthOfLIS(int[] nums) { + if (nums== null||nums.length==0) { + return 0; + } + //sizeArray[i]代表sizeArray[i]这个元素组成的最长子序列的长度为i+1 + int[] sizeArray = new int[nums.length]; + sizeArray[0] = nums[0];//第一个元素的最长子序列长度为1 + int maxSize = 0; + for (int i = 1; i < nums.length; i++) { + if (nums[i]>sizeArray[maxSize]) {//如果当前元素比最长子序列的尾部元素大 + maxSize++; + sizeArray[maxSize] = nums[i]; + } else if (nums[i]==sizeArray[maxSize]) {//等于尾部元素,那么不用更新 + continue; + } else {//小于尾部元素进行更新,在sizeArray中进行二分查找 + int left = 0; + int right = maxSize; + while (left < right) { + int mid = (left+right)/2; + if (sizeArray[mid] < nums[i]) { + left = mid+1; + } else { + right = mid; + } + } + //最后left的位置肯定就是需要进行插入的位置 + //left左边的元素都比nums[i]小 + sizeArray[left]=nums[i]; + } + } + return maxSize+1; +} +``` \ No newline at end of file diff --git a/docs/LeetCode1.md b/docs/LeetCode1.md new file mode 100644 index 0000000..bb25d1a --- /dev/null +++ b/docs/LeetCode1.md @@ -0,0 +1,2371 @@ +## LeetCode 热门100题-题解(下) + +##### 主要是记录自己刷题的过程,也方便自己复习 + +##### [第155题-最小栈](#第155题-最小栈) +##### [第160题-相交链表](#第160题-相交链表) + +##### [第142题-环形链表II](#第142题-环形链表II) +##### [第739题-每日温度](#第739题-每日温度) + +##### [第347题-前K个高频元素](#第347题-前K个高频元素) + +##### [第49题-字母异位词分组](#第49题-字母异位词分组) +##### [第32题-最长有效括号](#第32题-最长有效括号) +##### [第543题-二叉树的直径](#第543题-二叉树的直径) +##### [第79题-单词搜索](#第79题-单词搜索) +##### [第96题-不同的二叉搜索树](#第96题-不同的二叉搜索树) +##### [第239题-滑动窗口最大值](#第239题-滑动窗口最大值) +##### [第146题-LRU缓存机制](#第146题-LRU缓存机制) +##### [第236题-二叉树的最近公共祖先](#第236题-二叉树的最近公共祖先) +##### [第114题-二叉树展开为链表](#第114题-二叉树展开为链表) +##### [第84题-柱状图中最大的矩形](#第84题-柱状图中最大的矩形) +##### [第148题-排序链表](#第148题-排序链表) +##### [第617题-合并二叉树](#第617题-合并二叉树) +##### [第287题-寻找重复数](#第287题-寻找重复数) +##### [第152题-乘积最大子数组](#第152题-乘积最大子数组) +##### [第72题-编辑距离](#第72题-编辑距离) +##### [第139题-单词拆分](#第139题-单词拆分) +##### [第76题-最小覆盖子串](#第76题-最小覆盖子串) +##### [第124题-二叉树中的最大路径和](#第124题-二叉树中的最大路径和) +##### [第461题-汉明距离](#第461题-汉明距离) +##### [第128题-最长连续序列](#第128题-最长连续序列) +##### [第647题-回文子串](#第647题-回文子串) +##### [第337题-打家劫舍III](#第337题-打家劫舍III) +##### [第238题-除自身以外数组的乘积](#第238题-除自身以外数组的乘积) +##### [第207题-课程表](#第207题-课程表) +##### [第309题-最佳买卖股票时机含冷冻期](#第309题-最佳买卖股票时机含冷冻期) +##### [第416题-分割等和子集](#第416题-分割等和子集) +##### [第560题-和为K的子数组](#第560题-和为K的子数组) +##### [第448题-找到所有数组中消失的数字](#第448题-找到所有数组中消失的数字) +##### [第437题-路径总和III](#第437题-路径总和III) +##### [第338题-比特位计数](#第338题-比特位计数) +##### [第406题-根据身高重建队列](#第406题-根据身高重建队列) +##### [第538题-把二叉搜索树转换为累加树](#第538题-把二叉搜索树转换为累加树) +##### [第297题-二叉树的序列化与反序列化](#第297题-二叉树的序列化与反序列化) +##### [第438题-找到字符串中所有字母异位词](#第438题-找到字符串中所有字母异位词) +##### [第240题-搜索二维矩阵II](#第240题-搜索二维矩阵II) +##### [第494题-目标和](#第494题-目标和) +##### [第621题-任务调度器](#第621题-任务调度器) +##### [第581题-最短无序连续子数组](#第581题-最短无序连续子数组) + + +### 第155题-最小栈 +设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 + +push(x) —— 将元素 x 推入栈中。 +pop() —— 删除栈顶的元素。 +top() —— 获取栈顶元素。 +getMin() —— 检索栈中的最小元素。 + + +示例: + +输入: +["MinStack","push","push","push","getMin","pop","top","getMin"] +[[],[-2],[0],[-3],[],[],[],[]] + +输出: +[null,null,null,null,-3,null,0,-2] + +解释: +MinStack minStack = new MinStack(); +minStack.push(-2); +minStack.push(0); +minStack.push(-3); +minStack.getMin(); --> 返回 -3. +minStack.pop(); +minStack.top(); --> 返回 0. +minStack.getMin(); --> 返回 -2. + +##### 解题思路 +就是用一个最小栈来记录最小值 +```java +Stack stack; + Stack minStack; + /** initialize your data structure here. */ + public MinStack() { + stack = new Stack(); + minStack = new Stack(); + } + + public void push(int x) { + if(minStack.size()==0||x<=minStack.peek()) { + minStack.push(x); + } + stack.push(x); + } + + public void pop() { + if(stack.peek().equals(minStack.peek())) { + minStack.pop(); + } + stack.pop(); + } + + public int top() { + return stack.peek(); + } + + public int getMin() { + return minStack.peek(); + } +``` + +### 第160题-相交链表 + +编写一个程序,找到两个单链表相交的起始节点。 + +如下面的两个链表**:** + +[![img](../static/160_statement.png)](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/12/14/160_statement.png) + +在节点 c1 开始相交。 + +##### 解题思路 + +分别计算出链表A,链表B的长度,让长的链表先走n步,直到两个链表剩余节点长度一样,然后每个链表都走一步,直到节点相等,即为相交节点。 + +```java +public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + int lengthA=0; + ListNode nodeA = headA; + while(nodeA!=null) { + lengthA++; + nodeA=nodeA.next; + } + int lengthB=0; + ListNode nodeB = headB; + while(nodeB!=null) { + lengthB++; + nodeB=nodeB.next; + } + nodeA = headA; + nodeB = headB; + while(lengthA!=lengthB) { + if (lengthA>lengthB) { + lengthA--; + nodeA = nodeA.next; + } else { + lengthB--; + nodeB = nodeB.next; + } + } + while(nodeA!=null && nodeB!=null) { + if(nodeA == nodeB) { + return nodeA; + } + nodeA = nodeA.next; + nodeB = nodeB.next; + } + return null; + } +``` + +### 第142题-环形链表II + +给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 + +为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。 + +说明:不允许修改给定的链表。 + +进阶: + +你是否可以使用 O(1) 空间解决此题? + + +示例 1: + +![img](../static/circularlinkedlist-9062165.png) + +输入:head = [3,2,0,-4], pos = 1 +输出:返回索引为 1 的链表节点 +解释:链表中有一个环,其尾部连接到第二个节点。 + +##### 解题思路 + +```java +public ListNode detectCycle(ListNode head) { + //快慢节点在圆中相遇 + //然后慢节点在圆中走一圈,计算出圆的周长 + //快节点凑头 + if (head==null || head.next == null) { + return null; + } + ListNode slow = head.next; + ListNode quick = head.next.next; + //通过快慢指针,让两个指针在环中相遇 + while (quick!=slow) { + if (quick == slow && quick!=null) { + break; + } + slow = slow.next; + if (quick==null||quick.next == null) { + return null; + } + quick = quick.next; + quick = quick.next; + } + //计算环的长度 + int circleLength = 1; + ListNode copySlow = slow.next; + while (copySlow != slow) { + copySlow = copySlow.next; + circleLength++; + } + //快指针先走环的长度步 + quick = head; + while (circleLength>0) { + quick = quick.next; + circleLength--; + } + slow = head; + //慢指针出发,相遇的节点就是环的入口 + while (quick!=slow) { + quick = quick.next; + slow = slow.next; + } + return quick; + } +``` + +### 第739题-每日温度 + +请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。 + +例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。 + +提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。 + +##### 解题思路 +其实跟滑动窗口最大值那个题的解题思路很像,就是维护一个还没有排序好的栈,每次把栈中比当前遍历元素小的,都出栈,然后计算等待天数,然后将当前元素入栈。 +```java + public int[] dailyTemperatures(int[] T) { + if (T == null || T.length==0) { + return null; + } + int[] result = new int[T.length]; + Stack stack = new Stack<>(); + stack.add(0); + for (int i = 1; i < T.length; i++) { + //比栈顶元素小,栈 + if (stack.size() == 0 || T[i] <= T[stack.peek()]) { + stack.push(i); + } else { + // 比栈顶元素大,就一直出栈 + while (stack.size() >0 && T[i] > T[stack.peek()]) { + int index = stack.pop(); + result[index] = i - index; + } + stack.push(i); + } + } + return result; + } +``` + +### 第347题-前K个高频元素 + +给定一个非空的整数数组,返回其中出现频率前 k 高的元素。 + +示例 1: + +输入: nums = [1,1,1,2,2,3], k = 2 +输出: [1,2] +示例 2: + +输入: nums = [1], k = 1 +输出: [1] + +##### 解题思路 +先使用一个HashMap来统计各元素的频率,然后取前k个元素建立小顶堆,然后对后面的元素进行遍历,如果频率高于堆顶元素,就与堆顶元素交换,然后对堆进行调整。 +```java +public int[] topKFrequent(int[] nums, int k) { + HashMap map = new HashMap(); + for(int i = 0;i= 0; i--) { + adjustHeap(result,i,result.length,map); + } + for (int i = k; i < array.length ; i++) { + Integer key = (Integer) array[i]; + if (map.get(key) >= map.get(result[0])) { + result[0] = (Integer)array[i]; + adjustHeap(result,0,result.length,map); + } + } + return result; + } + void adjustHeap(int[] array, int i,int length,HashMap map) { + while (2*i+1> groupAnagrams(String[] strs) { + HashMap map = new HashMap(); + for (String str : strs) { + char[] charArray = str.toCharArray(); + Arrays.sort(charArray); + String sortedString = new String(charArray); + List strList = map.get(sortedString); + if (strList == null) { + strList = new LinkedList(); + } + strList.add(str); + map.put(sortedString,strList); + } + ArrayList totalList = new ArrayList<>(); + totalList.addAll(map.values()); + return totalList; + } +``` + +### 第32题-最长有效括号 + +给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。 + +示例 1: + +输入: "(()" +输出: 2 +解释: 最长有效括号子串为 "()" +示例 2: + +输入: ")()())" +输出: 4 +解释: 最长有效括号子串为 "()()" + +##### 解题思路 + +判断一个字符是否在是有效括号,就是对字符串遍历,如果是(,就将(字符入栈,如果是)字符,就判断栈是否为有元素,有元素代表括号匹配上了,将当前括号和栈顶元素括号都标记为有效括号。标记的方式是将recordArray数组中相应下标置为1,然后统计最长有效括号就是统计recordArray数组中连续1的个数。 + +```java + public int longestValidParentheses(String s) { + Stack stack = new Stack(); + + int[] recordArray = new int[s.length()]; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c=='(') { + stack.push(i); + } else if (c==')' && stack.size()>0) { + //说明这个位置可以匹配 + recordArray[i]=1; + int leftIndex = stack.pop(); + recordArray[leftIndex] = 1; + } + } + //统计recordArray张连续1的长度 + int max = 0; + int currentSize =0; + for (int i = 0; i < recordArray.length; i++) { + if (recordArray[i] ==0) { + currentSize=0; + } else if (recordArray[i] ==1) { + currentSize++; + } + max = max > currentSize ? max : currentSize; + } + return max; + } +``` + +### 第543题-二叉树的直径 + +给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。 + +示例 : +给定二叉树 + + 1 + / \ + 2 3 + / \ + 4 5 +返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。 + +##### 解题思路 +其实根节点的直径就是左节点深度+右节点深度,其他节点也是这个公示,所以可以递归求解出左右节点的深度,然后计算当前节点的直径,然后与左右节点的直径进行判断,返回最大值。 +```java +int maxDiameter = 0; +public int diameterOfBinaryTree(TreeNode root) { + if (root==null){return 0;} + maxDepth(root); + return maxDiameter; +} + +public int maxDepth(TreeNode root) { + if (root==null) {return 0;} + int leftDepth = maxDepth(root.left); + int rightDepth = maxDepth(root.right); + int diameter = leftDepth + rightDepth; + maxDiameter = diameter > maxDiameter ? diameter : maxDiameter; + return leftDepth>rightDepth?leftDepth+1 : rightDepth+1; +} +``` + +### 第79题-单词搜索 +给定一个二维网格和一个单词,找出该单词是否存在于网格中。 + +单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。 + + + +示例: + +board = +[ + ['A','B','C','E'], + ['S','F','C','S'], + ['A','D','E','E'] +] + +给定 word = "ABCCED", 返回 true +给定 word = "SEE", 返回 true +给定 word = "ABCB", 返回 false +##### 解题思路 +就是遍历数组,判断每个字符与字符串首字符是否相同,相同的话就继续判断周围的字符是否满足要求。需要注意的时,在判断时会把走过的路径添加到HashSet中去,防止一个位置的字符重复被使用。 +```java +HashSet set = new HashSet<>(); + public boolean exist(char[][] board, String word) { + if (board==null||board[0]==null||word==null||word.length()==0) { + return false; + } + char firstChar = word.charAt(0); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + set = new HashSet<>(); + if(board[i][j] == firstChar + && judgeTheChar(board, word,1,i,j) == true) { + return true; + } + } + } + return false; + } + + boolean judgeTheChar(char[][] board,String word,int index,int i,int j) { + + String key = i + "-" + j; + if (set.contains(key)) { + return false; + } else { + set.add(key); + } + if (index>= word.length()) { + return true; + } + char currentChar = word.charAt(index); + //上 + if (i-1>=0 && + board[i-1][j] == currentChar && judgeTheChar(board, word,index+1,i-1,j) == true) { + return true; + } + //下 + if (i+1=0 && + board[i][j-1] == currentChar + && judgeTheChar(board, word,index+1,i,j-1) == true) { + return true; + } + + if (j+1< board[0].length && + board[i][j+1] == currentChar + && judgeTheChar(board, word,index+1,i,j+1) == true) { + return true; + } + return false; + } +``` + +### 第96题-不同的二叉搜索树 +给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? + +示例: + +输入: 3 +输出: 5 +解释: +给定 n = 3, 一共有 5 种不同结构的二叉搜索树: + + 1 3 3 2 1 + \ / / / \ \ + 3 2 1 1 3 2 + / / \ \ + 2 1 2 3 + +##### 解题思路 +就是把一个二叉搜索树的组合种数,其实是左子树的组合数乘以右子树的组合数,假设n为4,f(n)代表组合种数,二叉树共有四个节点,那么二叉树肯定有根节点,组合主要分为以下几类: +1.左子树有0个节点,右子树有3个节点,f(0)*f(3) +2.左子树有1个节点,右子树有2个节点,f(1)*f(2) +3.左子树有2个节点,右子树有1个节点,f(2)*f(1) +4.左子树有3个节点,右子树有0个节点,f(3)*f(0) +所以,f(4)=f(0)*f(3)+f(1)*f(2)+f(2)*f(1)+f(3)*f(0) + +而f(0)=1,f(1)=1,f(2)=2 + +```java +public int numTrees(int n) { + int[] array = new int[n+1]; + array[0] = 1; + for (int i = 1; i <= n; i++) { + int num = 0; + for (int j = 0; j <=i-1; j++) { + num += array[j]*array[i-1-j]; + } + array[i] = num; + } + return array[n]; + } +``` + +递归解法 + +```java +int[] cacheArray; +public int numTrees(int n) { + if(n == 0 || n == 1){return 1;} + if(n==2) { + return 2; + } + if(cacheArray == null) { + cacheArray = new int[n+1]; + } else if (cacheArray[n] !=0) { + return cacheArray[n]; + } + int sum = 0; + for(int i = 0; i maxQueue = new LinkedList<>(); + for (int i = 0; i < nums.length; i++) { + //将队列中小于当前数的元素出队列 + while (maxQueue.size()>0 && nums[i] > nums[maxQueue.getLast()]) { + maxQueue.removeLast(); + } + maxQueue.add(i); + //窗口的左边界 + int left = i - k+1; + //将队列中出边界的最大值移除 + if (maxQueue.getFirst() =0) { + result[left] = nums[maxQueue.getFirst()]; + } + } + return result; + } + +``` + +### 第146题-LRU缓存机制 +运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制 。 +实现 LRUCache 类: + +LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存 +int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。 +void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。 + +##### 解题思路 +LinkedHashMap本身是基于HashMap做了一些扩展,就是通过链表将所有键值对连接起来了,链接的就是添加键值对的顺序。(新添加的键值对在链表尾部,put方法对于已存在的key进行值覆盖时,是不会修改键值对在链表中的顺序的) +所以我们可以基于LinkedHashMap实现LRU。 +LRU的get()方法 +1.map中不存在这个key,返回-1 +2.map中存在这个key,先remove这个键值对,再put操作添加这个键值对,再返回value。(这样可以修改键值对在链表中的顺序。) +LRU的put()方法 +1.已存在这个key,先remove,再put。 +2.不存在这个key,判断是否超过容量,超过将最后一个键值对移除,将键值对添加到map。 +```java +LinkedHashMap map = new LinkedHashMap(); +int capacity; + +public LRUCache(int capacity) { + this.capacity = capacity; +} + +public int get(int key) { + Integer value = map.get(key); + if (value == null) { + return -1; + } + map.remove(key); + map.put(key,value); + return value; +} + +public void put(int key, int value) { + Integer oldValue = map.get(key); + if (oldValue!=null) { + //只是覆盖value的话,put方法不会改变键值对在链表中的顺序,所以需要先remove + map.remove(key); + map.put(key,value); + return; + } + if (map.size()>=capacity) { + map.remove(map.keySet().iterator().next()); + } + map.put(key,value); +} +``` + +### 第236题-二叉树的最近公共祖先 + +给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 + +百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” + +例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4] + +示例 1: + +输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 +输出: 3 +解释: 节点 5 和节点 1 的最近公共祖先是节点 3。 +示例 2: + +输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 +输出: 5 +解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。 + +##### 解题思路 +其实所有的节点分为以下几种: +1.就是要寻找的节点1,或节点2 +2.此节点子树中中包含节点1,节点2其中的一个 +3.节点1,节点2全部位于此节点的左子树,或者是右子树 +4.此节点左子树包含节点1,右子树包含节点2 +所以第4种就是我们要寻找的节点,并且在二叉树中只有一个,所以我们对二叉树进行遍历,判断某个节点左子树,右子树都包含节点,那么就返回该节点。 + +```java +TreeNode lowestCommonAncestor(TreeNode root, TreeNode node1, TreeNode node2) { + if (root==null) { + return null; + } + if (root==node1 || root==node2) {//当前节点就是要找的节点之一 + return root; + } + TreeNode leftNode = lowestCommonAncestor(root.left,node1,node2);//判断左子树中是否有节点 + TreeNode rightNode = lowestCommonAncestor(root.right,node1,node2);//判断右子树中是否有节点 + if (leftNode!=null&&rightNode!=null) {//就是我们要找的节点 + return root; + } else if (leftNode!=null && rightNode==null) {//左子树中有节点,右子树没有节点,继续向上遍历 + return leftNode; + } else if (leftNode==null && rightNode!=null) {//继续向上遍历 + return rightNode; + } + return null; + } +``` + +### 第114题-二叉树展开为链表 + +给定一个二叉树,原地将它展开为一个单链表。 + +例如,给定二叉树 + +​ 1 + + / \ + 2 5 + / \ \ +3 4 6 +将其展开为: + +1 + \ + 2 + \ + 3 + \ + 4 + \ + 5 + \ + 6 + +##### 解题思路 + +其实就是先序遍历,需要注意的是,需要先将节点的左右节点保存,然后再进行修改操作。其次是需要将所有节点的left指针置为null. + + +```java + TreeNode lastNode;//lastNode用于保存上一个遍历的节点 + public void flatten(TreeNode root) { + if(root == null) {return;} + TreeNode left = root.left; + TreeNode right = root.right; + if(lastNode==null) { + lastNode = root; + lastNode.left=null; + } else { + lastNode.left=null; + lastNode.right = root; + lastNode=root; + } + flatten(left); + flatten(right); + } +``` + +### 第84题-柱状图中最大的矩形 + +给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 + +求在该柱状图中,能够勾勒出来的矩形的最大面积。 + +![img](../static/histogram.png) + + + +以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。 + +![img](../static/histogram_area.png) + + + +图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。 + +示例: + +输入: [2,1,5,6,2,3] +输出: 10 + +##### 解题思路 + +可以暴力求解,就对每个height[i]都计算包含它的元素的最大高度,这样复杂度是O(N^2),计算的方法主要是对于每个i找到左边最后一个大于height[i]的边界left,和右边最后一个大于height[i]的边界right, + +矩形面积=height[i]*(right-left+1),例如对于第五个柱子值为2的那个柱子来说,左边界left就是值为5的柱子,右边界就是值为3的柱子。 + +单调栈解法 + +理论就是假设f(i)代表包含圆柱i的最大矩形,那么其实就是在圆柱i的左边和右边各自找到第一个高度更小的圆柱k和j,f(i) = height[i]*(j-k+1)。所以可以使用单调栈来保存比当前圆柱高度height[i]小的元素,如果栈中元素高度比height[i]大,那么那些元素就需要出栈了。 + + + +可以使用一个栈Stack来保存左边圆柱高度比height[i]小的元素,也就是Stack中的值只能是比height[i]小的元素。栈底元素是最小的,栈顶元素高度是最大的。 + +遍历时,如果当前height[i]>栈顶元素的高度,说明当前栈顶元素还没有碰到比它小的数,所以还不能计算面积,就把height[i]入栈。 + +如果现在的height[i]<栈顶的元素高度小,说明栈顶元素碰到比它小的元素了,就需要出栈计算矩形面积, + + 面积=heights[j] * (i - left) + +```java +public int largestRectangleArea(int[] heights) { + if (heights==null||heights.length==0) { + return 0; + } + int maxArea = 0; + Stack stack = new Stack(); + stack.add(0); + for (int i = 1; i <= heights.length; i++) { + int currentH; + if (i==heights.length) { + //这是最后新增了一个0元素 + //防止整个数组是全体递增的,这样会计算不出面积 + currentH = 0; + } else { + currentH = heights[i]; + } + + if(currentH > heights[stack.peek()]) { + stack.push(i); + } else if (currentH == heights[stack.peek()]) { + stack.pop(); + stack.push(i); + } else {//当heights[i] < stack.peek()时,进行出栈计算 + while (stack.size()>0 && currentH0) { + leftIndex = stack.peek() + 1; + } else { + leftIndex = 0; + } + int area = heights[index] * (i - leftIndex); + maxArea = maxArea > area ? maxArea : area; + } + stack.push(i); + } + } + return maxArea; + } +``` + +### 第148题-排序链表 + +给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 + +进阶: + +你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗? + +示例 1: + +![img](../static/sort_list_1.jpg) + + +输入:head = [4,2,1,3] +输出:[1,2,3,4] + +##### 解题思路 + +就是归并排序 + +```java + ListNode preHead = new ListNode(1); + ListNode sortList(ListNode start) { + //归并排序划分到只有一个节点时,直接返回 + if (start.next == null || start==null) {return start;} + ListNode quick = start; + ListNode slow = start; + while (quick!=null) { + quick=quick.next; + if (quick.next==null){break;} + quick = quick.next; + slow = slow.next; + } + //4 2 1 3 + ListNode otherStart = slow.next; + slow.next = null; + //对链表分解和合并后,新链表的头结点不一定还是start + start = sortList(start); + otherStart = sortList(otherStart); + //开始merge + //preHead用于我们来保存新组成的链表的头结点 + ListNode currentNode = preHead; + while (start != null && otherStart!=null) { + if (start.valvalue?max:value; + times =0; + } + } + return max; + } + public int maxProduct(int[] nums,int start,int end,int times) { + if (start>end) { + return 0; + } + if (start==end) { + return nums[start]; + } + if (times%2 == 0) {//负数为偶数个 + return calculateValue(nums,start,end); + } else {// + //第一个负数的下标 + Integer firstNegativeIndex=null; + //最后一个负数的下标 + Integer lastNegativeIndex=null; + for (int i = start; i <= end; i++) { + if (nums[i] < 0 && firstNegativeIndex == null) { + firstNegativeIndex = i; + } + if (nums[i] < 0) { + lastNegativeIndex = i; + } + } + int vaule1 = calculateValue(nums,start,lastNegativeIndex-1); + int value2 = calculateValue(nums,firstNegativeIndex+1,end); + return vaule1>value2?vaule1:value2; + } + } + //计算start到end之间的元素乘积和 + int calculateValue(int[] nums, int start, int end) { + if (start>end) { + return 0; + } + int result = 1; + for (int i = start; i <= end; i++) { + result*=nums[i]; + } + return result; + } +``` + +### 第72题-编辑距离 + +给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。 + +你可以对一个单词进行如下三种操作: + +插入一个字符 +删除一个字符 +替换一个字符 + + +示例 1: + +输入:word1 = "horse", word2 = "ros" +输出:3 +解释: +horse -> rorse (将 'h' 替换为 'r') +rorse -> rose (删除 'r') +rose -> ros (删除 'e') + +##### 解题思路 +就是我们要把word1转换为word2,从word2第一个字符开始遍历,对于每一个字符而言,i,j分别为word1和word2当前遍历的位置有四种选择: +##### 1.直接跳过 +当words[i]与word[j]相同时,简单举例来说,就是假设要把"acc"转换为"abb",由于第一个字符相同,那么编辑的步数其实是等于"cc"转换为"bb"的步数 +所以假设使用restLength[i][j]代表剩余子串需要的步数, +``` +restLength[i][j] = restLength[i+1][j+1] +``` +##### 2.将word1[i]删除 +假设我们要将"abcd"转换为"bc",那么其实我们可以将a删除掉,相当于对于word1跳过了当前字符,继续遍历 +``` +restLength[i][j] = restLength[i+1][j] +``` +##### 3.在word1[i]前增加新字符word2[j] +假设我们要将"bbb"转换为"abbb",那么就可以对于word1增加一个word2当前的字符,然后继续遍历,相当于是对word2的字符跳过了 +``` +restLength[i][j] = restLength[i][j+1] +``` +##### 4.对word1[i]替换成word2[j] + +假设我们要将"abbb"转换为"cbbb",那么直接将a替换成c就好了,相当于对word1和word2字符串都跳过了当前字符 +``` +restLength[i][j] = restLength[i+1][j+1] +``` +代码 +```java + int[][] cacheLength; + public int minDistance(String word1, String word2) { + if (word1==null||word2==null) { + return 0; + } + cacheLength = new int[word1.length()+1][word2.length()+1]; + return minDistance(word1,word2,0,0); + } + + public int minDistance(String word1, String word2,int i,int j) { + //当有一个字符串已经走到末尾了,要变成另一个字符串只能是将另一个字符串剩余字符全部添加过来 + if (cacheLength[i][j]!=0) { + return cacheLength[i][j]; + } + if (i == word1.length()) { + return word2.length() - j; + } + if (j == word2.length()) { + return word1.length() - i; + } + if (word1.charAt(i) == word2.charAt(j)) {//相等就直接向后移动 + return minDistance(word1,word2,i+1,j+1); + } + //这一步选择删除,那么就是将word1的当前字符删除 + int deleteLength = minDistance(word1,word2,i+1,j) + 1; + //这一步选择插入,就是将word2的当前字符插入到word1 + int insertLength = minDistance(word1,word2,i,j+1) + 1; + //这一步走替换,就是将word2的当前字符替换到word1 + int replaceLength = minDistance(word1,word2,i+1,j+1) + 1; + //选择上面三种路线中最短的 + int min = deleteLength < insertLength ? deleteLength : insertLength; + min = replaceLength < min ? replaceLength : min; + cacheLength[i][j] = min; + return min; + } +``` + +### 第139题-单词拆分 +给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 + +说明: + +拆分时可以重复使用字典中的单词。 +你可以假设字典中没有重复的单词。 +示例 1: + +输入: s = "leetcode", wordDict = ["leet", "code"] +输出: true +解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。 +示例 2: + +输入: s = "applepenapple", wordDict = ["apple", "pen"] +输出: true +解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。 +  注意你可以重复使用字典中的单词。 + +##### 解题思路 +就是对于字符串"abc"来说,可以拆分成"a"和"bc",如果说字典中存在"a",我们只需要递归调用函数对剩余字符"bc"进行判断,判断"bc"是否能分解。最坏复杂度应该是O(N^2)。这里优化的点就是每次判断时,如果计算得到子串"bc"不能被分解,我们应该将它添加到falseSet,避免重复计算。 +```java +HashSet falseSet = new HashSet(); + public boolean wordBreak(String s, List wordDict) { + if (s==null||s.length()==0) {return true;} + if (falseSet.contains(s)) { + return false; + } + HashSet wordSet = new HashSet<>(wordDict); + for (int i = 0; i < s.length(); i++) { + //当前子串 + String subString = s.substring(0,i+1); + //剩余子串 + String restString = s.substring(i+1,s.length()); + //如果剩余子串存在,那么就分解成功了 + if (wordSet.contains(subString) && wordBreak(restString, wordDict)) { + return true; + } + } + //分解不成功,将子串添加到falseSet,避免重复计算 + falseSet.add(s); + return false; + } +``` + +### 第76题-最小覆盖子串 + +给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。 + +注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。 + +示例 1: + +输入:s = "ADOBECODEBANC", t = "ABC" +输出:"BANC" + +##### 解题思路 + +首先本题的最小子串是不需要保证子串的顺序的,也就是子串是ABC,我们的最小覆盖子串是BANC也可以,不一定非需要保证ABC的顺序。其实就是滑动窗口,我们用一个needMap来记录,key是需要查找的子串的字符,value是字符从次数。然后就用两个指针作为滑动窗口来遍历字符串,滑动窗口中字符及出现的次数用windowMap来存储, + +1.然后每次右指针移动,获取新进入窗口的字符,更新windowMap,如果当前字符是needMap中存在的,且windowMap该字符出现次数已达标,那么就更新needSize(子串中的字符在窗口中出现的次数)。 + +2.左指针进行移动,判断当前字符是否能移除窗口,(如果是子串需要的,且在窗口出现的次数<=子串需要的次数,就不能移除) + +3.判断当前needSize是否达标,达标说明当前窗口就是一个覆盖子串,如果比之前最小的覆盖子串小,那么就进行替换。 + +```java +public String minWindow(String s, String t) { + //needMap的key就是字符串t中出现的每个字符,value就是这个字符出现的次数 + HashMap needMap = new HashMap<>(); + //windowMap就是滑动窗口中当前字符及字符出现次数 + HashMap windowMap = new HashMap<>(); + for(int i = 0;i leftNeedTimes) {//需要这个字符,且所包含该字符个数大于需要的,可以左移 + windowMap.put(currentLeftChar,leftWindowTimes-1); + left++; + } else if(leftWindowTimes <= leftNeedTimes){//需要该字符,并且窗口不能左移 + break; + } + } + //判断当前窗口是否满足需求 + if(needSize >= t.length() && (minStr.equals("") || right-left+1 < minStr.length())) { + minStr = s.substring(left,right+1); + } + right++; + } + return minStr; + } + +``` + +### 第124题-二叉树中的最大路径和 +给定一个非空二叉树,返回其最大路径和。 + +本题中,路径被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。 + +示例 1: + +输入:[1,2,3] + + 1 + / \ + 2 3 + +输出:6 +示例 2: + +输入:[-10,9,20,null,null,15,7] + +  -10 +   / \ +  9  20 +    /  \ +   15   7 + +输出:42 +##### 解题思路 +这个题由于不要求路径开始和路径结束的节点是叶子节点,所以是有点复杂,我们可以先定义一个函数,这个函数可以计算每个节点单条路径的长度,就是可以是节点本身,也可以是节点+左子树中的节点连接,也可以是节点+左子树中的节点连接,但是节点不能既连接左节点,由连接右节点。在遍历过程中,可以得到(根节点,根节点+左子树路径最大值,根节点+右子树路径最大值)三者的最大值,然后进行返回。在遍历的过程中,由于我们知道左子树路径最大值和右子树路径最大值,我们可以计算以该节点为根节点的路径最大值,然后与totalMax进行比较和替换。 + +```java +//因为最大路径出现时,肯定是有一个根节点的,所以在计算单条路径时, + // 可以同时计算最大路径和,然后存储到totalMax + Integer totalMax = Integer.MIN_VALUE; + public int maxPathSum(TreeNode root) { + siglePath(root); + return totalMax; + } + + //这里计算的是单条路径长度,就是根节点+左路+右路的 + // 根节点,根节点+左节点中的路径,根节点+右节点中的路径 + //但是不能是左节点中的路径+根节点+右节点的路径 + public int siglePath(TreeNode root) { + if (root == null) { + return 0; + } + //左边单条路径的长度 + int leftPath = siglePath(root.left); + leftPath = leftPath>0?leftPath:0; + int rightPath = siglePath(root.right); + rightPath = rightPath>0?rightPath:0; + //根节点+左路大,还是根节点+右路大 + int max = leftPath > rightPath ? + leftPath + root.val : rightPath + root.val; + //实时计算左路单条路径+根节点+右路单条路径哪个大 + totalMax = leftPath+rightPath+root.val > totalMax ? leftPath+rightPath+root.val : totalMax; + return max; + } +``` + +### 第461题-汉明距离 + +两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。 +给出两个整数 x 和 y,计算它们之间的汉明距离。 +注意: +0 ≤ x, y < 2的31次方. + +示例: + +输入: x = 1, y = 4 + +输出: 2 + +解释: +1 (0 0 0 1) +4 (0 1 0 0) + ↑ ↑ +上面的箭头指出了对应二进制位不同的位置。 + +##### 解题思路 + +```java +public int hammingDistance(int x, int y) { + int result = x^y; + int num=0; + while(result>0) { + if((result&1) == 1) { + num++; + } + result = result>>1; + } + return num; +} +``` + +### 第128题-最长连续序列 + +给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 +进阶:你可以设计并实现时间复杂度为 O(n) 的解决方案吗? +示例 1: +输入:nums = [100,4,200,1,3,2] +输出:4 +解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。 +示例 2: +输入:nums = [0,3,7,2,5,8,4,6,0,1] +输出:9 + +##### 解题思路 +就是先使用HashMap存储各个值,然后遍历键值对,判断每个键值对周围的数字是否存在。这里做的优化点就是HashMap的key存的就是数组中出现的元素,value存的是一个区间,代表的是连续数组的左右边界。 +```java +public int longestConsecutive(int[] nums) { + HashMap> map = new HashMap<>(); + //遍历数组,key就是数组中的元素,value记录的是连续子数组的左右边界 + //value初始区间只包含一个元素,就是[key,key] + for (int i = 0; i < nums.length; i++) { + List list = new ArrayList<>(); + list.add(nums[i]); + list.add(nums[i]); + map.put(nums[i],list); + } + Integer max = 0; + //根据每个key去判断左右元素是否存在,计算最大区间 + for (Integer key: map.keySet()) { + List list = map.get(key); + int left = list.get(0)-1; + int right = list.get(1)+1; + //向左扩展 + while (map.containsKey(left)) { + List currentList = map.get(left); + list.set(0,currentList.get(0)); + left = currentList.get(0) - 1; + } + //向右扩展 + while (map.containsKey(right)) { + List currentList = map.get(right); + list.set(1,currentList.get(1)); + right = currentList.get(1) + 1; + } + int currentValue = list.get(1) - list.get(0) + 1; + max = max > currentValue ? max : currentValue; + } + return max; + } +``` + +### 第647题-回文子串 +给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 +具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。 + +示例 1: +输入:"abc" +输出:3 +解释:三个回文子串: "a", "b", "c" + +示例 2: +输入:"aaa" +输出:6 +解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa" +##### 解题思路 +这个题本身没有什么技巧,就是就是对每个字符,从中心往两边扩展,判断是否是回文串,一旦发现不是回文串,后面就不需要继续扩展了。需要注意的是,回文串分为奇数回文串,偶数回文串,所以中心可以是当前单个字符,也可以是当前字符+右边的字符。 +```java + int num=0; + public int countSubstrings(String s) { + char[] array = s.toCharArray(); + for (int i = 0; i < array.length; i++) { + //奇数回文串 + calculateNum(array,i,i); + //偶数回文串 + calculateNum(array,i,i+1); + } + return num; + } + + void calculateNum(char[] array,int start,int end) { + while (start>=0 && end value2 ? value1 : value2; + } else {//当前根节点不能被选择 + max = rob(root.left,true) + rob(root.right,true); + } + return max; + } +``` + +### 第238题-除自身以外数组的乘积 + +给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。 + +示例: + +输入: [1,2,3,4] +输出: [24,12,8,6] + +##### 解题思路 + +就是对于位置i,本题要求去求nums[0]*nums[1]...nums[i-1]nums[i+1]...nums[length-1],不能用除法,认识要求的这个乘积可以分成两部分,一部分是i以前的部分,nums[0]*nums[1]...nums[i-1],一部分是nums[i+1]...nums[length-1],所以可以先分别计算出left数组,对于每个元素存在0到i的乘积,计算right数组,对于每个元素,计算i到length-1的乘积。 + +```java + public int[] productExceptSelf(int[] nums) { + int[] left = new int[nums.length]; + int[] right = new int[nums.length]; + int[] result = new int[nums.length]; + int temp =1; + //left[i]存储的是nums[0]到nums[i]的乘积 + for (int i = 0; i < nums.length; i++) { + temp = temp*nums[i]; + left[i] = temp; + } + temp = 1; + //right[i]存储的是nums[i]到nums[length-1]的乘积 + for (int i = nums.length-1; i >=0 ; i--) { + temp = temp * nums[i]; + right[i] = temp; + } + for (int i = 0; i < nums.length; i++) { + int leftValue = i-1 >= 0 ? left[i-1] : 1; + int rightValue = i+1 < nums.length ? right[i+1] : 1; + result[i] = leftValue * rightValue; + } + return result; + } +``` + +### 第207题-课程表 + +你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。 + +在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1] + +给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习? + +示例 1: + +输入: 2, [[1,0]] +输出: true +解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。 +示例 2: + +输入: 2, [[1,0],[0,1]] +输出: false +解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。 + +##### 解题思路 +其实就是判断有向图是否存在环,有两种解法 +##### 深度优先遍历 +就是先根据二维数组构建一个邻接表,这里我们使用一个map来作为领接表,然后递归遍历map中的节点,对于图中每个节点有三种状态: + +1.未被访问过(在statusMap中值为null)。 + +2.已被访问过,并且它的子节点没有遍历访问完成(在statusMap中值为1)。 + +3.已被访问过,并且子节点也遍历访问完成(在statusMap中值为2)。 + +在递归遍历过程中,遇到上面第2种节点,说明就存在环。 + +```java +public boolean canFinish(int numCourses, int[][] prerequisites) { + HashMap> map = new HashMap<>(); + //构建邻接表 + for (int i = 0; i < prerequisites.length; i++) { + Integer key = prerequisites[i][0]; + List valueList = map.get(key); + if (valueList == null) { + valueList = new ArrayList<>(); + } + valueList.add(prerequisites[i][1]); + map.put(key,valueList); + } + HashMap statusMap = new HashMap<>(); + for (Integer key : map.keySet()) { + if (judgeIfHasCircle(map,statusMap,key)) {//有环 + return false; + } + } + return true; + } + //判断每个节点是否存在环 + boolean judgeIfHasCircle(HashMap> map, HashMap statusMap,Integer key) { + Integer status = statusMap.get(key); + if (status== null) { + statusMap.put(key,1); + } else if (status==1) { + return true; + } else if (status == 2) { + return false; + } + + List valueList = map.get(key); + if (valueList!=null) { + //遍历子节点 + for (Integer everyKey : valueList) { + if (judgeIfHasCircle(map, statusMap, everyKey)) { + return true; + } + } + } + //代表子节点遍历完毕 + statusMap.put(key,2); + return false; + } +``` +##### 拓扑排序 +这种解法有点像是宽度优先遍历,就是先建立邻接表,并且计算每个节点的入度,然后找到入度为0的节点(也就是没有被其他节点指向的节点),将它们入队列,然后对队列元素进行出队操作,取出队首元素,将它的子节点的入度都-1,然后子节点入度减到0时,就将这个子节点添加到队列中,在过程中会统计入过队列的节点数。原理就是如果没有环的,最终队列出队完成后,进入过队列的节点数是等于总节点数的。就是假设图的结构是1->2,2->3,3->4,4->2,也就是2,3,4形成一个环,最开始1是入度为0的节点,1会入队列,然后对节点2的入度-1,节点2的入度还剩下1,此时2不会入队列,所以最终进过队列的元素只有节点1,所以最终统计的数量是<总节点数的(如果不存在环,则所有节点的入度都会变成0,也就是结果集中的节点树会等于总节点数)。 + +### 第309题-最佳买卖股票时机含冷冻期 + +给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​ + +设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): + +你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 +卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 + +``` java +dp[i][0]//代表不持有股票 +dp[i][1]//代表持有股票 + + //如果今天不持有股票,要么是之前没有股票,要么是卖了股票 + dp[i][0] = max(dp[i-1][0],dp[i-1][1] + value[i]) + //如果今天持有股票,要么是之前就持有,要么是今天新买的 + dp[i][1] = max(dp[i-1][1],dp[i-2][0] - value[i]) + + +``` + + + +示例: + +输入: [1,2,3,0,2] +输出: 3 +解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出] + +##### 解题思路 + +这个跟上一题的区别就是有冷冻期,就是当你第i天要持有股票时,要么是第i-1天已持有股票,要么是第i-1天没有买卖股票才能在第天买股票。 +卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 + +```java + //如果今天不持有股票,要么是之前没有股票,要么是第i天卖了股票 + dp[i][0] = max(dp[i-1][0],dp[i-1][1] + value[i]) + //如果今天持有股票,要么是之前就持有,要么是第i天新买的 + dp[i][1] = max(dp[i-1][1],dp[i-2][0] - value[i]) +``` + +代码 + +```java + public int maxProfit(int[] prices) { + if(prices==null||prices.length<=1) {return 0;} + int[][] dp = new int[prices.length][2]; + dp[0][0] = 0; + dp[0][1] = 0 - prices[0]; + dp[1][0] = prices[1]-prices[0] < 0 ? 0 : prices[1]-prices[0]; + dp[1][1] = 0-prices[1] > 0- prices[0]? 0-prices[1] : 0-prices[0]; + for (int i = 2; i < prices.length; i++) { + dp[i][0] = Math.max(dp[i-1][1]+prices[i], dp[i-1][0]); + dp[i][1] = Math.max(dp[i-2][0]-prices[i],dp[i-1][1]); + } + return dp[prices.length-1][0]; + } +``` +这一题的时间复杂度为O(N),空间复杂度也为O(N),有一个可以优化的点就是d[i]只依赖于dp[i-1]和dp[i-2],所以理论上我们只需要一个几个常数变量就可以了,空间复杂度为O(1); +```java +public int maxProfit1(int[] prices) { + if(prices==null||prices.length<=1) {return 0;} + int last_last_0 = 0; + int last_0 = prices[1]-prices[0] < 0 ? 0 : prices[1]-prices[0]; + int last_1 = 0-prices[1] > 0- prices[0]? 0-prices[1] : 0-prices[0]; + for (int i = 2; i < prices.length; i++) { + int temp_0 = last_0; + last_0 = Math.max(last_1+prices[i], last_0); + last_1 = Math.max(last_last_0-prices[i],last_1); + last_last_0 = temp_0; + } + return last_0; + } +``` + +### 第416题-分割等和子集 +给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 + +注意: +每个数组中的元素不会超过 100 +数组的大小不会超过 200 +示例 1: +输入: [1, 5, 11, 5] +输出: true +解释: 数组可以分割成 [1, 5, 5] 和 [11]. +##### 解题思路 +本题就是可以转化为从数组中挑选i个元素,最终使和为数组和的一半,所以就转换为01背包问题了,只不过判断条件由选择让装的物品价值更大,变为装的物品的价值正好是总价值的一半。 +```java +public boolean canPartition(int[] nums) { + if (nums==null||nums.length==0) { + return false; + } + int sum = 0; + for (int i = 0; i < nums.length; i++) { + sum+=nums[i]; + } + if (sum%2==1) { + return false; + } else { + return canPartition(nums,nums.length-1,sum/2); + } + } + //使用HashMap缓存结果,避免重复计算 + HashMap resultCacheMap = new HashMap(); + //判断在0到i-1这些元素中进行能选择,看能否选择出的元素和为sum + public boolean canPartition(int[] nums,int i,int sum) { + if (sum<0) { return false; } + if (sum==0) { return true; } + if (i<0) { return false; } + if (i==0) {//只有一个元素了 + return nums[0]==sum; + } + String key = i+"-"+sum; + if (resultCacheMap.containsKey(key)) { + return resultCacheMap.get(key); + } + //选择元素i,看剩下的元素能否凑出sum 和不选择元素i + boolean result = canPartition(nums,i-1,sum-nums[i]) || canPartition(nums,i-1,sum); + resultCacheMap.put(key,result); + return result; + } +``` + +### 第560题-和为K的子数组 +给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。 +示例 1 : +输入:nums = [1,1,1], k = 2 +输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。 +说明 : +数组的长度为 [1, 20,000]。 +数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。 +##### 解题思路 +假设一个数组nums的元素为[a1,a2,a3,a4],假设我们使用f(a,b)代表从数组下标a到数组下标b的连续子数组和,f(a,b)=f(0,b)-f(0,a),也就是说假设子数组的和[a2,a3]=[a1,a2,a3]-[a1],所以k = [a1,a2,a3]-[a1],我们判断和为k的数量,其实也就判断从0开始的子树组之间的差为k的数量,所以我们计算将从下标为0的数组的和,添加到HashMap中去,然后遍历时进行判断。 +```java + public int subarraySum(int[] nums, int k) { + if (nums==null||nums.length==0) { return 0; } + int matchTimes=0; + int sum=0; + HashMap sumMap = new HashMap<>(); + //这个主要是为了统计为从0到i的和为sum的子数组 + sumMap.put(0,1); + for (int i = 0; i < nums.length; i++) { + sum+=nums[i]; + int key = sum - k; + if (sumMap.containsKey(key)) { + matchTimes+=sumMap.get(key); + } + //将当前sum和添加到map中去 + Integer times = sumMap.get(sum); + if (times==null) { + times=0; + } + sumMap.put(sum,times+1); + } + return matchTimes; + } +``` + +### 第448题-找到所有数组中消失的数字 +给定一个范围在  1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。 +找到所有在 [1, n] 范围之间没有出现在数组中的数字。 +您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。 +示例: + +输入: +[4,3,2,7,8,2,3,1] + +输出: +[5,6] +##### 解题思路 +这个题因为数字a的取值都是[1,nums.length]之间,所以a-1应该是在[0,nums.length-1]之间,正好跟数组的下标可以对应上,所以对数组遍历,将数字a放到下标a-1下,如果 +1.当前数字a如果为-1那么就不用调整位置了,因为这是我们设置的标志位,如果a正好等于下标i+1,也不用调整,因为是正确的位置 +2.如果下标a-1正好存的也是a,那么说明是出现两次的元素,那么将那个位置标志位-1,也不用调整了 +3.将当前元素a与下标a-1的元素交换,继续遍历。 +```java +public List findDisappearedNumbers(int[] nums) { + List list = new ArrayList<>(); + if (nums==null||nums.length==0) { + return list; + } + for (int i = 0; i < nums.length; i++) { + int index = nums[i] - 1; + if (nums[i] == -1 || nums[i] == i+1) {//说明是未出现过的数字,或者是已经调整到正确位置的数字,直接跳过 + continue; + } else if (nums[index] == index+1) {//说明这个位置已经有这个元素了,是出现两次的元素 + nums[i] = -1; + } else {//否则是出现一次的元素,进行交换 + int temp = nums[index]; + nums[index] = nums[i]; + nums[i] = temp; + i--; + } + } + for (int i = 0; i < nums.length; i++) { + if (nums[i] == -1) { + list.add(i+1); + } + } + return list; + } +``` + +### 第437题-路径总和III + +给定一个二叉树,它的每个结点都存放着一个整数值。 + +找出路径和等于给定数值的路径总数。 + +路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 + +二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。 + +示例: + +root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 + + 10 + / \ + 5 -3 + / \ \ + 3 2 11 + / \ \ +3 -2 1 + +返回 3。和等于 8 的路径有: + +1. 5 -> 3 +2. 5 -> 2 -> 1 +3. -3 -> 11 + +##### 解题思路 +其实就是对每个节点作为起始节点,开始向下遍历,计算路径和,每当路径和为sum时,就对数量+1。 +``` +int pathNum = 0; + //这个方法主要负责对二叉树遍历 + public int pathSum(TreeNode root, int sum) { + if (root==null) { return 0; } + //必须包含根节点的 + pathSumMustHasRoot(root,sum,0); + //不包含根节点的 + pathSumMustHasRoot(root.left,root.val); + pathSumMustHasRoot(root.right,root.val); + return pathNum; + } + //对每个节点计算路径和,然后继续向下 + void pathSumMustHasRoot(TreeNode root,int sum,int currentSum) { + if (root == null) {return ;} + currentSum+=root.val; + if (currentSum == sum) { + pathNum++; + } + pathSumMustHasRoot(root.left,sum,currentSum); + pathSumMustHasRoot(root.right,sum,currentSum); + } +``` + +### 第338题-比特位计数 +给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。 + +示例 1: + +输入: 2 +输出: [0,1,1] +示例 2: + +输入: 5 +输出: [0,1,1,2,1,2] +##### 解题思路 +因为i&(i-1)的结果相当于是去掉了最右边的一个1,所以 +i中1的数量 = i&(i-1)中1的数量 + 1,所以可以使用一个数组保存以前的数的1的数量,这样就可以以O(1)的时间复杂度计算中1的数量。 +```java +public int[] countBits(int num) { +//i & (i-1)可以将最右边的0去掉 + int[] bitCountArray = new int[num+1]; + bitCountArray[0] = 0; + for (int i = 1; i <= num; i++) { + bitCountArray[i] = bitCountArray[i&(i-1)] +1; + } + return bitCountArray; + } +``` + +### 第406题-根据身高重建队列 +假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。 + +请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。 + +示例 1: + +输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]] +输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] +解释: +编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。 +编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。 +编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。 +编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。 +编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。 +编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。 +因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。 + +##### 解题思路 +```java + public int[][] reconstructQueue(int[][] people) { + if (people==null||people[0]==null) { + return null; + } + //按照身高从大到小排列,身高相同,k从小到大排列 + //排序前:[[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]] + //排序后:[[7,0],[7,1],[6,1],[5,2],[5,0],[4,4]] + Arrays.sort(people, new Comparator() { + @Override + public int compare(int[] o1, int[] o2) { + // return o1[0] != o2[0] ? o1[0]-o2[0] : o2[1] - o1[1]; + return o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0]; + } + }); + List list = new ArrayList<>(people.length); + for (int[] i : people) { + list.add(i[1],i); + } + return list.toArray(new int[list.size()][2]); + } +``` + +### 第538题-把二叉搜索树转换为累加树 + +给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 + +提醒一下,二叉搜索树满足下列约束条件: + +节点的左子树仅包含键 小于 节点键的节点。 +节点的右子树仅包含键 大于 节点键的节点。 +左右子树也必须是二叉搜索树。 +注意:本题和 1038: https://leetcode-cn.com/problems/binary-search-tree-to-greater-sum-tree/ 相同 + +![img](../static/tree.png) + +示例 1: + +输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8] +输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8] + +##### 解题思路 + +这个题其实就是对二叉树按照右子树-根节点+左子树的顺序进行遍历,并且记录之前遍历的节点的和,然后当前节点值=之前的节点和+当前值 + +```java + int sum = 0; + public TreeNode convertBST(TreeNode root) { + if (root==null) return null; + convertBST(root.right); + sum +=root.val; + root.val = sum; + convertBST(root.left); + return root; + } +``` + +### 第297题-二叉树的序列化与反序列化 + +序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。 + +请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。 + +示例:  + +你可以将以下二叉树: + +​ 1 + + / \ + 2 3 + / \ + 4 5 + +序列化为 "[1,2,3,null,null,4,5]" +提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。 + +说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。 + +##### 解题思路 +```java + + String serialize(TreeNode root) { + StringBuffer stringBuffer = new StringBuffer(); + if (root == null) {return stringBuffer.toString();} + ArrayList queue = new ArrayList(); + queue.add(root); + while (queue.size()>0) { + TreeNode node = queue.remove(0); + if (node == null) { + stringBuffer.append("#!"); + } else { + stringBuffer.append(node.val+"!"); + queue.add(node.left); + queue.add(node.right); + } + } + return stringBuffer.toString(); +} + +TreeNode deserialize(String str) { + if (str == null || str.length() == 0) {return null;} + String[] array = str.split("!"); + + Integer rootValue = convert(array[0]); + if (rootValue == null) {return null;} + + TreeNode rootNode = new TreeNode(rootValue); + ArrayList queue = new ArrayList(); + queue.add(rootNode); + int currentIndex = 1; + while (queue.size()>0 && currentIndex findAnagrams(String s, String p) { + List list = new ArrayList<>(); + if (s==null||p==null) { + return list; + } + HashMap needMap = new HashMap<>(); + for (int i = 0; i < p.length(); i++) { + Character c = p.charAt(i); + Integer times = needMap.get(c); + times = times == null ? 1 : times + 1; + needMap.put(c,times); + } + int left=0; + int right =0; + int valid_num = 0; + HashMap windowsMap = new HashMap<>(); + while (left<=right && right目标值,需要排除更大的数,由于这一列都是比目标值大的,都需要排除掉。 + +3.当前值<目标值,需要排除更小的数,由于这一行都是比目标值小的,都需要排除掉。 + +```java +public boolean searchMatrix(int[][] matrix, int target) { + if (matrix==null||matrix[0]==null) { + return false; + } + int rowength = matrix.length; + int colLength = matrix[0].length; + int i = 0,j = colLength-1; + while (i < rowength && j >=0) { + if (matrix[i][j] == target) { + return true; + } else if (matrix[i][j] > target) {//当前值>目标值,需要排除更大的数 + j--;//排除这一列 + } else if (matrix[i][j] < target) {//当前值<目标值,需要排除更小的数 + i++;//排除这一行 + } + } + return false; + } +``` + +### 第494题-目标和 + +给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。 + +返回可以使最终数组和为目标数 S 的所有添加符号的方法数。 +示例: + +输入:nums: [1, 1, 1, 1, 1], S: 3 +输出:5 +解释: + +-1+1+1+1+1 = 3 ++1-1+1+1+1 = 3 ++1+1-1+1+1 = 3 ++1+1+1-1+1 = 3 ++1+1+1+1-1 = 3 + +一共有5种方法让最终目标和为3。 +##### 解题思路 +假设前面添加+的数组成的子数组为x,前面加-的数组成的子数组为y, +那么 +```java +x+y=sum //sum为数组之和 +x-y=S +``` +所以x= (sum+S)/2,所以问题变成了从数组中选取一些数,它们的和的为(sum+S)/2,所以转换为01背包问题了,变成从n个物品中取x个物品,正好价值等于某个数。 +```java + int result =0; + public int findTargetSumWays(int[] nums, int S) { + if (nums==null||nums.length==0) {return 0;} + int sum=0; + for (int i = 0; i < nums.length; i++) { + sum+=nums[i]; + } + if ((sum + S)%2==1) {//是奇数,不可能有结果 + return 0; + } + findTargetSumWays(nums,(sum+S)/2,nums.length-1); + return result; + } + public void findTargetSumWays(int[] nums, int sum,int i) { + if (sum<0) { + return; + } + //到最后一个元素了,不能继续递归选择了 + if (i==0) { + //如果等于sum,那么选择元素i,将result+1, + //如果sum为0,那么不选择元素i,将result+1 + if (nums[i] == sum) { + result++; + } + if (sum==0) { + result++; + } + return; + } + //不选这个元素 + findTargetSumWays(nums,sum,i-1); + //选这个元素 + findTargetSumWays(nums,sum-nums[i],i-1); + } +``` +### 第621题-任务调度器 +给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。 + +然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。 + +你需要计算完成所有任务所需要的 最短时间 。 +示例 1: + +输入:tasks = ["A","A","A","B","B","B"], n = 2 +输出:8 +解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B + 在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。 + +##### 解题思路 +假设没有某个时间点需要待命,那么任务所需要的最短时间就是任务的总数量,就是tasks.length。如果有某个时间点需要待命,那么一定是出现次数最多的那种任务导致的,此时就根据次数最多的任务来计算最大值。 +当没有时间需要待命时, +最短时间=tasks.length +需要待命时,出现次数最多的那种任务只有1种时, +最短时间=(maxCount-1)*(n+1)+1 +需要待命时,出现次数最多的那种任务有x种时, +最短时间=(maxCount-1)*(n+1)+x +```java +public int leastInterval(char[] tasks, int n) { + int[] count = new int[26]; + for (int i = 0; i < tasks.length; i++) { + int index = tasks[i] - 'A'; + count[index]++; + } + //找出频率最大的字符 + int maxCount=0; + //频率最大的字符有几个 + int times=0; + for (int i = 0; i < count.length; i++) { + if (count[i] > maxCount) { + maxCount = count[i]; + times=1; + } else if(count[i] == maxCount) { + times++; + } + } + int max = (maxCount-1)*(n+1) + 1 + times - 1; + max = max > tasks.length ? max : tasks.length; + return max; + } +``` + +### 第581题-最短无序连续子数组 + +给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。 + +你找到的子数组应是最短的,请输出它的长度。 + +示例 1: + +输入: [2, 6, 4, 8, 10, 9, 15] +输出: 5 +解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。 +说明 : + +输入的数组长度范围在 [1, 10,000]。 +输入的数组可能包含重复元素 ,所以升序的意思是<=。 +##### 解题思路 +这个题就是寻找逆序对,找出需要最左边的需要调整的逆序对位置,然后再找出最右边的逆序对位置,两者之间的距离就是子数组的长度。 +```java +public int findUnsortedSubarray(int[] nums) { + if (nums==null||nums.length==0) { + return 0; + } + //从左往右开始遍历,记录最大值,找到需要调整的最右边的位置的下标 + Integer right = null; + int maxIndex = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] < nums[maxIndex]) {//说明是需要调整的 + right=i; + } + maxIndex = nums[i] > nums[maxIndex] ? i : maxIndex; + } + //从右往左开始遍历,记录最小值,找到需要调整的最右边的位置的下标 + Integer left = null; + int minIndex = nums.length-1; + for (int i = nums.length-1; i >=0; i--) { + if (nums[i] > nums[minIndex]) {//说明是需要调整的 + left=i; + } + minIndex = nums[i] < nums[minIndex] ? i : minIndex; + } + if (left!=null&& right!=null) { + return right-left+1; + } + return 0; + } + + +``` + diff --git a/docs/Lock.md b/docs/Lock.md new file mode 100644 index 0000000..1f1fb21 --- /dev/null +++ b/docs/Lock.md @@ -0,0 +1,488 @@ +# 锁专题 + +#### [1.sychronize的实现原理是怎么样的?](#sychronize的实现原理是怎么样的?) +#### [2.AbstractQueuedSynchronizer(缩写为AQS)是什么?](#AbstractQueuedSynchronizer(缩写为AQS)是什么?) + +#### [3.悲观锁和乐观锁是什么?](#悲观锁和乐观锁是什么?) + +### sychronize的实现原理是怎么样的? + +```java +public class SyncTest { + public void syncBlock(){ + synchronized (this){ + System.out.println("hello block"); + } + } + public synchronized void syncMethod(){ + System.out.println("hello method"); + } +} +``` + +当SyncTest.java被编译成class文件的时候,`synchronized`关键字和`synchronized`方法的字节码略有不同,我们可以用`javap -v` 命令查看class文件对应的JVM字节码信息,部分信息如下: + +```java +{ + public void syncBlock(); + descriptor: ()V + flags: ACC_PUBLIC + Code: + stack=2, locals=3, args_size=1 + 0: aload_0 + 1: dup + 2: astore_1 + 3: monitorenter // monitorenter指令进入同步块 + 4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; + 7: ldc #3 // String hello block + 9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V + 12: aload_1 + 13: monitorexit // monitorexit指令退出同步块 + 14: goto 22 + 17: astore_2 + 18: aload_1 + 19: monitorexit // monitorexit指令退出同步块 + 20: aload_2 + 21: athrow + 22: return + Exception table: + from to target type + 4 14 17 any + 17 20 17 any + + + public synchronized void syncMethod(); + descriptor: ()V + flags: ACC_PUBLIC, ACC_SYNCHRONIZED //添加了ACC_SYNCHRONIZED标记 + Code: + stack=2, locals=1, args_size=1 + 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; + 3: ldc #5 // String hello method + 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V + 8: return + +} +``` + +对于`synchronized`关键字而言,`javac`在编译时,会生成对应的`monitorenter`和`monitorexit`指令分别对应`synchronized`同步块的进入和退出,有两个`monitorexit`指令的原因是为了保证抛异常的情况下也能释放锁,所以`javac`为同步代码块添加了一个隐式的try-finally,在finally中会调用`monitorexit`命令释放锁。 + +而对于`synchronized`方法而言,`javac`为其生成了一个`ACC_SYNCHRONIZED`关键字,在JVM进行方法调用时,发现调用的方法被`ACC_SYNCHRONIZED`修饰,则会先尝试获得锁。 + +#### synchronized锁执行流程图 + +这是网上看到的一个流程图: + +![sychronize](../static/sychronize.png) + +就是Java对象的内存布局其实由对象头+实例数据+对齐填充三部分组成,而对象头主要包含Mark Word+指向对象所属的类的指针组成。Mark Word主要用于存储对象自身的运行时数据,哈希码,GC分代年龄,锁标志等。 + +![img](../static/640的副本) + +下面就是Mark Word的数据映射表 + +![image](../static/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f31312f32382f313637353964643162306239363236383f773d37323026683d32353026663d6a70656726733d3337323831.jpeg) + +#### 偏向锁 + +根据上面的表来看,Mark Word后三位为101时,加锁对象的状态为偏向锁,偏向锁的意义在于同一个线程访问sychronize代码块时不需要进行加锁,解锁操作,性能开销更低(HotSpot[1]的作者经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。) + +因为正常情况下,当一个线程访问同步块并获取轻量级锁时,需要进行CAS操作将对象头的锁记录里指向当前线程的栈中的锁记录,执行完毕后需要释放轻量级锁。如果是同一个线程多次访问sychronize代码块,多次获取和释放轻量级,开销会偏大,所以会一开始判断对象是无锁状态,会将对象头设置为偏向锁,并且这个的线程ID添加到锁对象的Mark Word中,后续同一线程判断加锁标志是偏向锁,并且线程ID一致就可以直接执行。偏向锁的加锁过程: + +##### 场景一:当锁对象第一次被线程获得锁的时候 + +线程发现是匿名偏向状态(也就是锁对象的Mark Word没有存储线程ID),则会用CAS指令,将 `mark word`中的thread id由0改成当前线程Id。如果成功,则代表获得了偏向锁,继续执行同步块中的代码。否则,将偏向锁撤销,升级为轻量级锁。 + +##### 场景二:当获取偏向锁的线程再次进入同步块时 + +发现锁对象存储的线程ID就是当前线程的ID,会往当前线程的栈中添加一条 `DisplacedMarkWord`为空的 `LockRecord`中,然后继续执行同步块的代码,因为操纵的是线程私有的栈,因此不需要用到CAS指令;由此可见偏向锁模式下,当被偏向的线程再次尝试获得锁时,仅仅进行几个简单的操作就可以了,在这种情况下, `synchronized`关键字带来的性能开销基本可以忽略。 + +##### 场景二:当没有获得锁的线程进入同步块时 + +当没有获得锁的线程进入同步块时,发现当前是偏向锁状态,并且存储的是其他线程ID(也就是其他线程正在持有偏向锁),则会进入到**撤销偏向锁**的逻辑里,一般来说,会在 `safepoint`中去查看偏向的线程是否还存活 + +- 如果线程存活且还在同步块中执行, 则将锁升级为轻量级锁,原偏向的线程继续拥有锁,只不过持有的是轻量级锁,继续执行代码块,执行完之后按照轻量级锁的解锁方式进行解锁,而其他线程则进行自旋,尝试获得轻量级锁。 +- 如果偏向的线程已经不存活或者不在同步块中, 则将对象头的 `mark word`改为无锁状态(unlocked) + +由此可见,偏向**锁升级**的时机为:**当一个线程获得了偏向锁,在执行时,只要有另一个线程尝试获得偏向锁,并且当前持有偏向锁的线程还在同步块中执行,则该偏向锁就会升级成轻量级锁。** + +#### 偏向锁的解锁过程 + +因此偏向锁的解锁很简单,其仅仅将线程的栈中的最近一条 `lockrecord`的 `obj`字段设置为null。需要注意的是,偏向锁的解锁步骤中**并不会修改锁对象Mark Word中的thread id,简单的说就是锁对象处于偏向锁时,Mark Word中的thread id 可能是正在执行同步块的线程的id,也可能是上次执行完已经释放偏向锁的thread id**,主要是为了上次持有偏向锁的这个线程在下次执行同步块时,判断Mark Word中的thread id相同就可以直接执行,而不用通过CAS操作去将自己的thread id设置到锁对象Mark Word中。**这是偏向锁执行的大概流程:**![img](../static/640-20201128120828308) + +#### 轻量级锁 + +重量级锁依赖于底层的操作系统的Mutex Lock来实现的,但是由于使用Mutex Lock需要将当前线程挂起并从用户态切换到内核态来执行,这种切换的代价是非常昂贵的,而在大部分时候可能并没有多线程竞争,只是多个线程交替执行,(例如:这段时间是线程A执行同步块,另外一段时间是线程B来执行同步块,仅仅是多线程交替执行,并不是同时执行,也没有竞争),如果采用重量级锁效率比较低。以及在重量级锁中,没有获得锁的线程会阻塞,获得锁之后线程会被唤醒,阻塞和唤醒的操作是比较耗时间的,如果同步块的代码执行比较快,等待锁的线程可以进行先进行自旋操作(就是不释放CPU,执行一些空指令或者是几次for循环),等待获取锁,这样效率比较高。所以轻量级锁天然瞄准不存在锁竞争的场景,如果存在锁竞争但不激烈,仍然可以用自旋锁优化,自旋失败后再升级为重量级锁。 + +##### 轻量级锁的加锁过程 + +JVM会为每个线程在当前线程的栈帧中创建用于存储锁记录的空间,我们称为Displaced Mark Word。如果一个线程获得锁的时候发现是轻量级锁,会把锁的Mark Word复制到自己的Displaced Mark Word里面。 + +然后线程尝试**用CAS操作将自己线程栈中拷贝的锁记录的地址写入到锁对象的Mark Word中**。如果成功,当前线程获得锁,如果失败,表示Mark Word已经被替换成了其他线程的锁记录,说明在与其它线程竞争锁,当前线程就尝试使用**自旋**来获取锁。 + +自旋:不断尝试去获取锁,一般用循环来实现。 + +自旋是需要消耗CPU的,如果一直获取不到锁的话,那该线程就一直处在自旋状态,白白浪费CPU资源。 + +JDK采用了适应性自旋,简单来说就是线程如果自旋成功了,则下次自旋时触发重量级锁的阀值会更高,如果自旋失败了,则自旋的次数就会减少。 + +自旋也不是一直进行下去的,如果自旋到一定程度(和JVM、操作系统相关),依然没有获取到锁,称为自旋失败,那么这个线程会阻塞。同时这个锁就会升级成重量级锁。 + +#### 轻量级锁的释放流程 + +在释放锁时,当前线程会使用CAS操作将Displaced Mark Word的内容复制回锁的Mark Word里面。如果没有发生竞争,那么这个复制的操作会成功。如果有其他线程因为自旋多次导致轻量级锁升级成了重量级锁,那么CAS操作会失败,此时会释放锁并唤醒被阻塞的线程。**轻量级锁的加锁解锁流程图:** + +![img](../static/640-20201128120828359) + +#### 重量级锁 + +每个对象都有一个监视器monitor对象,重量级锁就是由对象监视器monitor来实现的,当多个线程同时请求某个重量级锁时,重量级锁会设置几种状态用来区分请求的线程: + +**Contention List 竞争队列**:所有请求锁的线程将被首先放置到该竞争队列,我也不知道为什么网上的文章都叫它队列,其实这个队列是先进后出的,更像是栈,就是当Entry List为空时,Owner线程会直接从Contention List的队列尾部(后加入的线程中)取一个线程,让它成为OnDeck线程去竞争锁。(主要是刚来获取重量级锁的线程是会进行自旋操作来获取锁,获取不到才会进入Contention List,所以OnDeck线程主要与刚进来还在自旋,还没有进入到Contention List的线程竞争) + +**Entry List 候选队列**:Contention List中那些有资格成为候选人的线程被移到Entry List,主要是为了减少对Contention List的并发访问,因为既会添加新线程到队尾,也会从队尾取线程。 + +**Wait Set 等待队列**:那些调用wait()方法被阻塞的线程被放置到Wait Set。 + +**OnDeck**:任何时刻最多Entry List中只能有一个线程被选中,去竞争锁,该线程称为OnDeck线程。 + +**Owner**:获得锁的线程称为Owner。 + +**!Owner**:释放锁的线程。 + +##### 重量级锁执行流程: + +流程图如下:![img](../static/640-6536508.png) + +**步骤1**是线程在进入Contention List时阻塞等待之前,程会先尝试自旋使用CAS操作获取锁,如果获取不到就进入Contention List队列的尾部(所以不是公平锁)。 + +**步骤2**是Owner线程在解锁时,如果Entry List为空,那么会先将Contention List中队列尾部的部分线程移动到Entry List。(所以Contention List相当于是后进先出,所以也是不公平的) + +**步骤3**是Owner线程在解锁时,如果Entry List不为空,从Entry List中取一个线程,让它成为OnDeck线程,Owner线程并不直接把锁传递给OnDeck线程,而是把锁竞争的权利交给OnDeck,OnDeck需要重新竞争锁,JVM中这种选择行为称为 “竞争切换”。(主要是与还没有进入到Contention List,还在自旋获取重量级锁的线程竞争) + +**步骤4**就是OnDeck线程获取到锁,成为Owner线程进行执行。 + +等待和通知步骤(这是调用了wait()和notify()方法才有的步骤): + +在同步块中,获得了锁的线程调用锁对象的Object.wait()方法,就是Owner线程调用锁对象的wait()方法进行等待,会移动到Wait Set中,并且会释放CPU资源,也同时释放锁, + +就是当其他线程调用锁对象的Object.notify()方法,之前调用wait方法等待的这个线程才会从Wait Set移动到Entry List,等待获取锁。 + +##### 3.为什么说是轻量级,重量级锁是不公平的? + +偏向锁由于不涉及到多个线程竞争,所以谈不上公平不公平,轻量级锁获取锁的方式是多个线程进行自旋操作,然后使用**用CAS操作将锁的Mark Word中存储的Lock Word替换为指向自己线程栈中拷贝的锁记录的指针**,所以谁能获得锁就看运气,不看先后顺序。重量级锁不公平主要在于刚进入到重量级的锁的线程不会直接进入Contention List队列,而是自旋去获取锁,所以后进来的线程也有一定的几率先获得到锁,所以是不公平的。 + +##### 4.重量级锁为什么需要自旋操作? + +因为那些处于ContetionList、EntryList、WaitSet中的线程均处于阻塞状态,阻塞操作由操作系统完成(在Linxu下通过pthreadmutexlock函数)。线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能。如果同步块中代码比较少,执行比较快的话,后进来的线程先自旋获取锁,先执行,而不进入阻塞状态,减少额外的开销,可以提高系统吞吐量。 + +##### 5.什么时候会发生锁升级,锁降级? + +**偏向锁升级为轻量级锁:**就是有不同的线程竞争锁时。具体来看就是当一个线程发现当前锁状态是偏向锁,然后锁对象存储的Thread id是其他线程的id,并且去Thread id对应的线程栈查询到的lock record的obj字段不为null(代表当前持有偏向锁的线程还在执行同步块)。那么该偏向锁就会升级成轻量级锁。 + +**轻量级锁升级为重量级锁:**就是在轻量级锁中,没有获取到锁的线程进行自旋,自旋到一定次数还没有获取到锁就会进行锁升级,因为自旋也是占用CPU的,长时间自旋也是很耗性能的。 + +**锁降级**因为如果没有多线程竞争,还是使用重量级锁会造成额外的开销,所以当JVM进入SafePoint安全点(可以简单的认为安全点就是所有用户线程都停止的,只有JVM垃圾回收线程可以执行)的时候,会检查是否有闲置的Monitor,然后试图进行降级。 + +##### 6.偏向锁,轻量锁,重量锁的适用场景,优缺点是什么? + +偏向锁:加锁解锁不需要进行CAS操作,适合一个线程多次访问同步块的场景。 + +轻量级锁:加锁和解锁使用CAS操作,没有像重量级锁那样底层操作系统的互斥量来加锁解锁,不涉及到用户态和内核态的切换和线程阻塞唤醒造成的线程上下文切换。没有获得锁的线程会自旋空耗CPU,造成一些开销。适合多线程竞争比较少,但是会有多线程交替执行的场景。 + +重量级锁:使用到了底层操作系统的互斥量来加锁解锁,但是会涉及到用户态和内核态的切换和线程阻塞和唤醒造成的线程上下文切换,但是不会自旋空耗CPU。 + +![image-20200516203659737](../static/image-20200516203659737.png) + +参考文章: + +[死磕Synchronized底层实现--概论](https://github.com/farmerjohngit/myblog/issues/12) + +[浅谈偏向锁、轻量级锁、重量级锁](https://www.jianshu.com/p/36eedeb3f912) + +### AbstractQueuedSynchronizer(缩写为AQS)是什么? + +AQS就是AbstractQueuedSynchronizer,抽象队列同步器,是一个可以用于实现基于先进先出等待队列的锁和同步器的框架。实现锁 +ReentrantLock,CountDownLatch,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。 + +ReentrantLock其实就是有一个变量sync,Sync父类是AbstractQueuedSynchronizer + +```java +public class ReentrantLock implements Lock, java.io.Serializable { + private final Sync sync; +} +``` + +ReentrantLock的非公平锁与公平锁的区别在于非公平锁在CAS更新state失败后会调用tryAcquire()来判断是否需要进入同步队列,会再次判断state的值是否为0,为0会去CAS更新state值,更新成功就直接获得锁,否则就进入等待队列。(进等待队列之前会抢锁) + +而公平锁首先判断state是否为0,为0并且等待队列为空,才会去使用CAS操作抢占锁,抢占成功就获得锁,没成功并且当前线程不是获得锁的线程,都会被加入到等待队列。 + +参考资料: + +深入理解ReentrantLock的实现原理 + +### synchronized锁与ReentrantLock锁的区别? + +**相同点:** + +1.可重入性 + +两个锁都是可重入的,持有锁的线程再次申请锁时,会对锁的计数器+1。 + +**不同点:** + +**1.实现原理** + +synchronized是一个Java 关键字,是由JVM实现的,底层代码应该是C++代码。而ReentrantLock是JDK实现的,是Java提供的一个类库,代码是Java代码,源码实现更加方便阅读。 + +**2.性能** + +在以前,synchronized锁的实现只有重量级锁一种模式,性能会比较差,后面引入了偏向锁和轻量级锁后就优化了很多。根据测试结果,在线程竞争不激烈的情况下,ReentrantLock与synchronized锁持平,竞争比较激烈的情况下,ReentrantLock会效率更高一些。 + +**3.功能** + +synchronized只能修饰方法,或者用于代码块,而ReentrantLock的加锁和解锁是调用lock和unlock方法,更加灵活。 + +其次是synchronized的等待队列只有一个(调用wait()方法的线程会进入等待队列),而ReentrantLock可以有多个条件等待队列。可以分组唤醒需要唤醒的线程们,而不是像synchronized要么用notify方法随机唤醒一个线程要么用notifyAll方法唤醒全部线程。ReentrantLock 提供了一种能够中断等待锁的线程的机制,就是线程通过调用lock.lockInterruptibly()方法来加锁时,一旦线程被中断,就会停止等待。 + +ReentrantLock可以使用tryLock(long timeout, TimeUnit unit)方法来尝试申请锁,设置一个超时时间,超过超时时间,就会直接返回false,而不是一直等待锁。 + +ReentrantLock可以响应中断,而synchronized锁不行 + +**4.公平性** + + synchronized锁是非公平锁,ReentrantLock有公平锁和非公平锁两种模式。 + +https://www.codercto.com/a/22884.html + +### ReentrantLock的加锁流程是怎么样的? + +ReentrantLock非公平锁的加锁流程: + +1.尝试着使用CAS操作将锁的状态state由0修改为1,修改成功则线程获得锁。 + +2.不成功就会再次尝试去抢锁,以及判断这个线程是否是当前持有锁的线程(如果是只需要将state+1,代表锁重入)。 + +3.抢锁没成功,也不是持有锁的线程,那么就会添加到等待队列然后调用Lock.Support.park()方法进行阻塞等待,然后被唤醒。 + +![图片](../static/640-3893313.png) + +公平锁加锁过程: + +1.如果当前锁没有被其他线程持有,并且等待队列中也没有其他线程等待,那么就使用CAS操作去抢锁。 + +2.或者线程就是当前持有锁的线程,那么就对state+1,代表锁重入。 + +3.以上情况都不是,就加入到等待队列进行等待。 + +非公平锁解锁流程 + +![图片](../static/640-20210221154207974.png) + +https://blog.csdn.net/qq_14996421/article/details/102967314 + +https://blog.csdn.net/fuyuwei2015/article/details/83719444 + +### 谈一谈你对AQS的理解? + +AQS是AbstractQueuedSynchronizer的缩写,是一个抽象同步队列类,可以基于它实现一个锁,例如ReentrantLock,只是需要实现tryAcquire()方法(也就是获取资源的方法,判断当前线程能否申请到独占锁)和tryRelease()方法(也就是释放资源的方法,在线程释放锁前对state进行更新),AQS会根据tryAcquire()的返回结果,来进行下一步的操作, + +如果为true,代表线程获得锁了。 + +如果为false,代表线程没有获得锁,由AQS负责将线程添加到CLH等待队列中,并且进行阻塞等待。当前一个线程释放锁时,AQS对这个线程进行唤醒。 + +(不同的自定义同步器争用共享资源的方式也不同。**自定义同步器在实现时只需要实现资源state的获取与释放方法即可**,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了) + +**公平锁加锁过程** + +加锁方法调用栈 + +```java +ReentrantLock lock = new ReentrantLock(); +lock.lock();//在我们代码中通过调用lock方法进行加锁 +FairSync.lock();//lock方法中会调用公平锁类FairSync的lock方法 +AbstractQueuedSynchronizer.acquire(1);//FairSync继承于Sync,而Sync继承于AQS,FairSync的lock方法中调用了acquire方法也就是去申请独占锁资源。 +public final void acquire(int arg) { + if (!tryAcquire(arg) && + acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) + selfInterrupt(); +} +FairSync.tryAcquire()//调用FairSync的tryAcquire去尝试着申请独占锁资源 +AbstractQueuedSynchronizer.addWaiter()//申请失败就将线程添加到等待队列尾部 +AbstractQueuedSynchronizer.acquireQueued()//并且让这个线程进入阻塞等待状态 +``` + +对于加锁,**ReentrantLock**只需要去实现**tryAcquire**()方法,去根据state判断当前线程能不能获取锁,能获取就会返回true。不能获取就会返回false,然后由AQS来将未获得锁的线程添加到CLH队列尾部,然后等待被唤醒。(公平锁与非公平锁也就在于**ReentrantLock的tryAcquire**实现的区别,当锁被其他线程占用时,公平锁是只有当前等待队列没有其他线程时,才能去抢锁,而非公平锁则没有这个限制,在申请锁时就能去抢锁。) + + + +可以看到AQS的acquire()方法中是会先去调用tryAcquire()去尝试着申请独占锁资源,AQS默认的tryAcquire()方法只有一行代码,会抛出UnsupportedOperationException异常(强制子类对这个方法进行实现)。所以ReentrantLock的FairSync对tryAcquire()方法进行了实现。 + +tryAcquire()返回true就代表获取独占锁资源成功: + +1.等待队列没有其他线程且这个线程CAS操作设置state成功 + +2.当前线程就是持有锁的线程,那么只需要对state+1即可。 + +tryAcquire()返回false代表获取独占锁资源失败, + +那么就调用AQS.addWaiter()方法申请失败就将线程添加到等待队列尾部,AQS.acquireQueued()方法让这个线程进入阻塞等待状态(在阻塞之前如果等待队列只有这一个线程,是会先尝试着获取锁,失败才会进入阻塞状态。) + +```java +//公平锁的tryAcquire方法实现 +protected final boolean tryAcquire(int acquires) { + final Thread current = Thread.currentThread(); + int c = getState(); + if (c == 0) { + //等待队列中没有线程,使用cas操作去抢锁,抢锁成功,就返回true + if (!hasQueuedPredecessors() && + compareAndSetState(0, acquires)) { + setExclusiveOwnerThread(current); + return true; + } + } + //当前线程与持有锁的线程是同一个,那么进行重入 + else if (current == getExclusiveOwnerThread()) { + int nextc = c + acquires; + if (nextc < 0) + throw new Error("Maximum lock count exceeded"); + setState(nextc); + return true; + } + return false; + } +``` + +**公平锁解锁过程:** + +解锁方法调用栈 + +```java +ReentrantLock lock = new ReentrantLock(); +lock.unlock(); +AbstractQueuedSynchronizer.release(1); +//AbstractQueuedSynchronizer的release()方法 +public final boolean release(int arg) { + if (tryRelease(arg)) { + Node h = head; + if (h != null && h.waitStatus != 0) + unparkSuccessor(h); + return true; + } + return false; + } +//ReentrantLock中Sync的tryRelease方法 +protected final boolean tryRelease(int releases) { + int c = getState() - releases; + if (Thread.currentThread() != getExclusiveOwnerThread()) + throw new IllegalMonitorStateException(); + boolean free = false; + //state为0就释放锁,否则只是锁的state减去releases + if (c == 0) { + free = true; + setExclusiveOwnerThread(null); + } + setState(c); + return free; + } +``` + +对于解锁,ReentrantLock的公平锁和非公平锁的实现是一样的,都是对state赋以最新的值,然后由AQS的unparkSuccessor方法负责对线程进行唤醒。 + +https://www.cnblogs.com/waterystone/p/4920797.html + +### 悲观锁和乐观锁是什么? + +### 悲观锁 + +就是假定在每次取数据的时候会修改这个数据,所以在取数据的时候就会进行加锁,这样其他调用者就不能取数据,阻塞等待,一直到获取到锁。Java中的同步锁sychronized和ReentrantLock就是悲观锁思想的实现。 + +##### 乐观锁和悲观锁的区别: + +悲观锁适合多写的场景 + +乐观锁适合多读的场景,这样只有读写冲突会发生的比较少,减少加锁的性能开销。但是如果是多写的场景,这样会导致上层应用一直重试,增加性能开销。 + +#### 乐观锁 + +就是假定在每次取数据时不会修改这个数据,所以在取数据的时候不会加锁,只有在真正修改数据时才加锁。Java中的atomic原子变量类就是乐观锁的实现。 + +##### 版本号机制 + +使用版本号来实现,对数据加上一个版本号,代表修改次数,每次修改后+1,修改数据时判断数据的版本号跟之前取的是否一致,一致才修改,不一致就重试,直到更新成功。 + +##### CAS操作 + +就是在更新数据时会传入之前取的值,在内存中判断当前内存中的值跟之前的值是否一致,一致再更新,(比较和更新都是在一个原子操作中)。 + +##### ABA问题怎么解决? + +使用CAS更新是没法解决ABA的问题,就是其他调用方对数据修改成其他值后又改回原来的值。AtomicStampedReference的compareAndSet会先判断对象的引用是否相同,相同才进行CAS更新。实现原理主要是AtomicStampedReference会保存之前对象的的引用,及一个修改版本号,只有当引用和版本号都相等的情况下,才会进行CAS更新操作。 + +##### CAS操作的缺点有哪些? + +1.循环时间长开销大 + +自旋CAS操作如果不成功就一直循环执行直到成功,如果长时间不成功,会给CPU带来非常大的执行开销 + +2.CAS 只对单个共享变量有效 + +多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用`AtomicReference类`把多个共享变量合并成一个共享变量来操作 + +#### Java的原子类 + +原子类一共有以下四种 + +- 1.基本类型: AtomicInteger, AtomicLong, AtomicBoolean ; +- 2.数组类型: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray ; +- 3.引用类型: AtomicReference, AtomicStampedRerence, AtomicMarkableReference ; +- 4.对象的属性修改类型: AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater 。 + +##### AtomicInteger + +主要是对Integer的封装,提供了一些原子性的操作,因为如果是使用Integer来完成i=i+1;操作,在内存中是三个步骤,先将从内存中取出i,放到寄存器中,然后将寄存器中的值与1相加,然后将结果写入内存,一共是三个步骤,所以不是原子性的,并发时会造成数据不一致的问题。 + +主要实现原理是AtomicInteger类有一个unsafe属性,可以通过unsafe来调用Unsafe类的一些原子性的方法Unsafe.compareAndSwapInt来实现原子性的加减运算。 + +其次是使用volatile来修饰value属性,保证一个内存可见性 + +```java +//compareAndSwapInt有四个参数,第一个是待运算的对象,第二个是对象中用于运算的属性的偏移量,第三个是期望值,第四个是更新的值。 +unsafe.compareAndSwapInt(this, valueOffset, expect, update) +``` + +```java +public class AtomicInteger extends Number implements java.io.Serializable { + private static final long serialVersionUID = 6214790243416807050L; + + // setup to use Unsafe.compareAndSwapInt for updates + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long valueOffset; + + static { + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } + } + + private volatile int value;//使用volatiole来保证value的内存可见性 +} + +``` + +在Unsafe类中,compareAndSwapInt和getAndAddInt的区别在于,getAndAddInt会一直重试直到成功,compareAndSwapInt如果更新失败,只会返回false + +```java +public final int getAndAddInt(Object var1, long var2, int var4) { + int var5; + do { + //var1是对象,var2是对象上某个变量的偏移量, + //var5就是对象var1上偏移量为var2的一个变量 + var5 = this.getIntVolatile(var1, var2); + //当var5的值没有变化时,就会进行加法操作,也就是var5 = var5 + var4 + //如果var5的值变化了,就会取最新的var5的值,进行加法操作。 + } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); + + return var5; +} +``` \ No newline at end of file diff --git a/docs/MySQLBook1.md b/docs/MySQLBook1.md new file mode 100644 index 0000000..6072684 --- /dev/null +++ b/docs/MySQLBook1.md @@ -0,0 +1,1140 @@ +(PS:建了一个技术微信群,可以自由地讨论技术,工作和生活,也会分享一些我自己在看的技术资料,不定时发放红包福利,欢迎大家扫[首页里面的二维码](README.md)进群,希望和大家一起学习进步!大家如果想一起为这个项目做贡献的话,也可以进群大家聊一聊) + + +### 为什么写这篇文章 +因为我最近的一些工作内容跟数据分析比较密切,所以需要对SQL使用得比较熟练,所以便阅读了《MySQL 必知必会》这本书,为了检验自己的阅读效果及帮助一些跟我一样需要学习MySQL相关的知识的朋友,所以每阅读一章,我就开始写一章的读书笔记,并在掘金的读书笔记版块发布沸点。一共三十章,全部发布完以后,便汇总成了这篇文章,感兴趣的朋友通过阅读这篇文章,快速得预览这本书,学习MySQL相关的知识。如果你觉得这本书对你带来帮助,希望你可以为我点一个关注,后续也会继续阅读其他的技术书籍,并整理成读书笔记,分享给大家。同时也欢迎大家加我[掘金主页](https://juejin.im/user/5b370a42e51d4558ce5eb969)的微信,我们一起探讨学习。 +### 第一章 了解SQL + +本章主要是介绍了一些数据库相关的概念: + +数据库:保存有组织的数据的容器。 + +表:某种特定类型数据的结构化清单。 + +模式(schema):关于数据库和表的布局及特性的信息。在MYSQL中,模式与数据库同义。 + +主键:唯一标识表中每行的这个列称为主键。一个列成为主键必须满足以下条件: + +1.唯一性,任意两行都不具有相同的主键值。 + +2.不为空,每行数据必须具有一个主键值。 + +### 第二章 MySQL简介 + +DBMS(数据库管理软件)通常分为两类: + +1.基于共享文件系统的DBMS。通常用于桌面(例如Microsoft Access和FileMaker) + +2.Client-Sever的DBMS。日常见到的MySQL,Oracle,SQL Server数据库都是这种类型的。Client主要负责与用户进行交互,接受用户的指令,然后发出请求给Server,Server负责数据访问和处理,然后将结果返回给Client。 + +### 第三章 使用MySQL + +主要介绍了一些MySQL的一些命令 + +use crashcourse;选择一个名叫crashcourse数据库(在通过命令行连接到数据库时,我们需要选择一个数据库,然后才能继续操作) +``` +show DATABASES;//展示当前可用的数据库列表 + +show Tables;//展示当前是选择的是数据库的所有表 + +show COLUMNS FORM customers;//展示customers表所有的列信息(会包含字段名,类型,是否允许为NULL,键信息,默认值,其他信息), +``` + +DESCRIBE customers;跟show COLUMNS FORM同义,用于展示表的列信息 +``` +SHOW STATUS;展示服务器信息 +SHOW CREATE DATABASE crashcourse; +``` +展示之前创建crashcourse这个数据库时使用的SQL语句,同理,SHOW CREATE TABLE也可以展示建某张表时使用的SQL语句 +``` +SHOW GRANTS FOR 'jeffrey'@'localhost';//展示jeffrey这个账号的权限 + +SHOW ERRORS和SHOW WARNINGS,//用来显示服务器错误或警告消息 +``` + +HELP SHOW;当你不了解某个命令时,可以使用HELP+这个命令,来获得一些说明信息,了解这个命令的用途,这里HELP SHOW会打印出SHOW命令的用法 + +### 第四章 检索数据 + +这两章主要讲得是查询相关的。 + +查询时默认的数据顺序: +``` +SELECT prod_name FROM products; +``` + +如果是不设置任何排序条件,以这种方式来进行查询,返回的数据的顺序是根据它们在底层表中出现的顺序(可以是数据最初添加到表中的顺序,但是如果数据进行过更新或删除,顺序会受到MySQL重用回收存储空间的影响) + +#### 使用DISTINCT去重: +``` +SELECT DISTINCT vend_id FROM products; +``` + +如果想要对让返回的数据不包含重复值,可以使用DISTINCT来对列进行修饰 +``` +SELECT DISTINCT vend_id,prod_price FROM products; +``` + +DISTINCT关键字是对所有字段进行修饰的,只有当所有列都相同时,才会进行排除,在上面这个例子中,只有vend_id和prod_price都相同的数据,才会进行排除,也就是可以允许一些vend_id相同,prod_price不同的数据出现。 + +#### 使用LIMIT来限制结果 +``` +SELECT prod_name FROM products LIMIT 5; +``` + +可以限制返回的数据为5条 +``` +SELECT prod_name FROM products LIMIT 4,5; +``` + +可以限制返回的数据是从第4行开始后面的5条 + +上面这条查询语句,MySQL 5以后还支持另外一种更加容易理解的写法 +``` +SELECT prod_name FROM products LIMIT 4 offset 5; +``` +使用完全限定的表名 +``` +SELECT products.prod_name FROM crash_course.products; +``` + +可以限制在某个数据库的某个表中进行查询,上面的例子是限制了,必须在crash_course数据库的products表取prod_name列的数据 + + +### 第五章 排序检索数据 + +这一章主要讲的是ORDER BY对查询结果进行排序,以及使用ASC,DESC控制升序,降序。 + +#### 使用ORDER BY进行排序 +``` +SELECT prod_name FROM products ORDER BY product_name; +``` + +#### 按多个列进行排序 +``` +SELECT prod_id, prod_price, prod_name FROM products ORDER BY product_price, product_name; +``` +在 ORDER BY 指定多个字段,可以按规定的顺序,按多个列排序,例子中的数据会先根据 product_price 从低到高进行排序,如果 product_price相同,再按 product_name ,就进行比较从A到Z进行排序,如下图所示 + +#### 指定排序方向 +默认的排序方向是升序,也就是ASC,有时候需要进行降序排序,例如价格从高到低进行排序,可以使用降序DESC + +### 第六章 过滤数据 + +这一章其实主要讲得是WHERE语句对数据进行过滤。 + +#### 条件判断符 + +一些常见的WHERE语句的条件判断符,大家已经知道了。 +例如: + +``` += 等于 +!= 不等于 +< 小于 +> 大于 +<= 小于等于 +>= 大于等于 +BETWEEN 在指定两个值之间 +除了上面这些,有一个不太常见的操作符号 +<>,代表不等于,与!= 同义 +``` + +#### != 与 IS NULL + +!= 是返回不具备特定值的行,NULL值代表未知,所以不会拿NULL值去跟特定值比较,所以不会具有NULL值的行。如果想要获取具有NULL值的行,必须使用IS NULL +例如: + +对下面这个表执行 SELECT * FROM table WHERE value != 100; + +| id | value | +| ---- | ----- | +| 1 | 100 | +| 2 | NULL | +| 3 | 200 | + +返回的结果: + +只会返回value为200的这一行,不会返回值为NULL的行 + +| id | value | +| ---- | ----- | +| 3 | 200 | + +#### BETWEEN + +使用BETWEEN操作符会匹配范围中所有的值,包括指定的开始值和结束值 + +例如: + +![](../static/01b10b5a725f31ceaac7247bb9dd0306.png) + +### 第七章 数据过滤 + +这一章主要说的是AND,OR, IN,NOT这四个操作符, + +#### 计算次序 +![](../static/2b995dc63cc6f999a810396859e9f868.png) + + +组合AND和OR使用时,因为AND优先级最高计算时会优先处理AND操作符,会将AND两边的条件进行提取,所以上面这个SQL语句其实会等价于 + +``` +SELECT prod_name, prod_price FROM products WHERE vend_id = 1002 OR (vend_id = 1003 AND prod_price >= 10); +``` + +可能会与我们想要的结果会有一定差距,我们是想要 + +``` +SELECT prod_name, prod_price FROM products WHERE vend_id = 1002 OR (vend_id = 1003 AND prod_price >= 10); +``` + +可能会与我们想要的结果会有一定差距,我们是想要vend_id为1002或1002,且prod_price大于10的数据,所以在日常使用中,最好使用()明确地分组相应的操作符,而不是依赖操作符的优先级,像下面这样: + +``` +SELECT prod_name, prod_price FROM products WHERE (vend_id = 1002 OR vend_id = 1003) AND prod_price >= 10; +``` + +#### IN操作符 + +![image-20190623173234935.png](../static/12609483-af8591f99a0ef451.png) + +#### OR操作符 +![image-20190623173257225.png](../static/12609483-fff5e4e51fd4b74f.png) + +在指定条件范围进行匹配时,IN和OR都能满足需求,但是IN有一些优点: + +1.IN操作符语法更加简洁直观,容易管理 + +2.IN操作符执行更快。 + +3.IN的最大优点是可以包含其他SELECT语句,从而可以动态地简历WHERE语句,第14章会对此进行详细介绍。 + +#### NOT操作符 + +其他DBMS允许使用NOT对各种条件取反,但在MySQL中,只支持使用NOT对IN、BETWEEN和EXISTS子句取反。 + +![image-20190623174919095.png](../static/12609483-247cddd987b8a7dc.png) + + +### 第八章 用通配符进行过滤 + +这一章主要是介绍了LIKE操作符,以及%,_ 这两个通配符。 + +#### LIKE操作符 + +LIKE主要是配合通配符一起使用的,LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式。 + +#### %通配符 + +%代表搜索模式中给定位置的0个、1个或多个字符。 +![image.png](../static/12609483-b15a13b54ef52bcd.png) +在一个查询语句中也可以使用多个%通配符 +![image.png](../static/12609483-3d649e6e34f13ce9.png) +![image.png](../static/12609483-3e2f092a320b1904.png) + +#### 尾空格 +可能会干扰通配符匹配。例如,在保存词 anvil时,如果它后面有一个或多个空格,则子句WHERE prod_name LIKE '%anvil'将不会匹配它们,因为在最后的l 后有多余的字符。解决这个问题的一个简单的办法是在搜索模式最后附加一个%。一个更好的办法是使用函数(第11章将会 介绍)去掉首尾空格。 +注意NULL +虽然似乎%通配符可以匹配任何东西,但有一个例 外,即NULL。即使是WHERE prod_name LIKE '%'也不能匹配 用值NULL作为产品名的行。 +下划线(_)通配符 +_通配符与%通配符类似,只不过只能匹配单个字符,不能匹配0个字符,也不能匹配多个字符 +![image.png](../static/12609483-bd97bcf1a54970ab.png) + +#### 使用通配符的技巧 +1.在能使用其他操作符的请款下,尽量不要使用通配符,因为它的搜索事件要比其他操作符的长 + +2.尽量不要把通配符用在搜索模式的开始处。放在搜索模式的开始处,搜索起来是最慢的。 + +3.仔细检查通配符的位置。如果放错地方,可能不会返回想要的数据。 + +### 第九章 用正则表达式进行搜索 +这一章主要讲得是正则相关的知识,我个人认为把正则当成一门单独的技术进行学习会比较好,所以建议可以专门去学习 + +### 第十章 使用计算字段 +某些场景下,存储在的表中的数据不是我们所需要的,我们需要对它进行转换、计算或格式化过,这就是计算字段的用途。 +#### 使用Concat()函数对字段进行拼接 +多数DBMS使用+或||来实现拼接, MySQL则使用Concat()函数来对字段进行拼接。Concat()可以将多个字符串拼接成一个,如下图所示: +![image](../static/12609483-525097b9e86c4bcb.png) + +#### 使用Trim()函数来去除空格 +Trim()函数 :去除字符串左右两边的空格 +LTrim()函数 :去除字符串左边的空格 +RTrim()函数 :去除字符串右边的空格 +下面是使用RTrim()函数的例子 +![image.png](../static/12609483-c93badac7f87621c.png) + +#### 使用别名 +我们使用Concat()函数拼接出来的字段是没有名字的,可以使用AS关键字给它赋予一个名字,当然当已有的字段包含不符合规定的字符时,也可以AS关键字给一个已有字段起别名。 +![image.png](../static/12609483-5758b86fedd81704.png) + +#### 执行算术计算 + +除了使用Concat()函数得到一个计算字符,也可以使用+,-,*,/计算得到一个字段。如图所示:* + +![](../static/0894a336d0cfbf05c51cd8b41d1a8090.png) + + +expanded_price列为一个计算字段,是由 +quantity*item_price计算得到的。 + +### 第十一章 使用数据处理函数 + +除了使用SQL语句对数据进行处理,还可以使用一些函数对数据进行处理,需要注意的是,函数没有SQL的可移植性那么强。 + +#### 文本处理函数 +![image.png](../static/12609483-892ba4177926f667.png) + +![image.png](../static/12609483-efff2ee9978ac306.png) + +使用案例: +这是使用Upper()函数将文本处理成大写的案例 +![](../static/bd319063b4d107251f125efe1cf25d9b.png) + +上面这些常见函数大家可能都能够理解,只有Soundex()不太常见,SOUNDEX是一个将任何文 本串转换为描述其语音表示的字母数字模式的算法。 +![image.png](../static/12609483-1ef1d215da9c4aa4.png) +如上图所示,假设有一个顾客的cust_contact值为Y.Lee,但是我们不知道Y.Lee,只知道这个顾客的名字的发音近似于Y.Lie,这个时候我们可以使用Soundex()将cust_contact列值转换为它的SOUNDEX值,因为Y.Lee和 Y.Lie发音相似,所以它们的SOUNDEX值匹配,因此可以查询到这个顾客。 + +#### 常用日期和时间处理函数 +![image.png](../static/12609483-05767921ed3dad2a.png) +举例,使用Date函数提取日期部分: +![image.png](../static/12609483-4ecd4a7222b8642c.png) + +#### 数值处理函数 +![image](../static/12609483-e796b57f35c2bacc.png) + +### 第十二章 汇总数据 +在日常开发中,我们除了获得检索得到的数据,还可以使用聚合函数对数据汇总,得到处理后的结果。 + +#### SQL聚合函数 +![image.png](../static/12609483-1707e382f5058cf9.png) + +#### AVG()函数 +AVG()是计算特定列的平均值,会忽略掉值为NULL的列。 +![image.png](../static/12609483-ce42aae525de4f33.png) +AVG()函数也可以搭配DISTINCT关键字使用,将重复的数据去重后,然后计算平均值,如下图所示: +![image.png](../static/12609483-8f63c4fe8cf743f5.png) +在使用了DISTINCT后,此例子中的avg_price比较高,因为有多个物品具有相同的较低价格。排除它们提升了平均价格。 + +#### COUNT()函数 +两种用法: +1.使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。 +2.使用COUNT(column)对特定列中具有值的行进行计数,会忽略 NULL值。 + + +### 第十三章 分组数据 +这一章主要讲了如果使用GROUP BY 对数据进行分组。 + +#### 创建分组 +![image.png](../static/12609483-d18fbaa9eb21d3cf.png) + +#### 过滤分组 +如果要对分组进行过滤,我们可以使用WHERE语句对表中数据进行过滤后,然后使用GROUP BY进行分组,也可以在使用GROUP BY进行分组后,再使用HAVING语句过滤掉一些分组。 +例如: +| id | value | +| ---- | ----- | +| 1 | 100 | +| 2 | 150 | +| 3 | 200 | +| 3 | 500 | +对于上面这个表的数据,如果我们想要过滤掉id为3的分组,那么可以写成使用WHERE语句的方式: +``` +SELECT id,COUNT(*) FROM table WHERE id != 3 GROUP BY id; +``` +也可以写成使用HAVING语句的方式: +``` +SELECT id,COUNT(*) FROM table GROUP BY id HAVING id !=3; +``` +当然在过滤分组这方面,HAVING要比WHERE更加强大,比如我们想要对数据分组,并且得到数量大于2的组,那么WHERE就无法实现,只能用HAVING语句。如下: +![image.png](../static/12609483-4e2f87d8d19f6349.png) + +#### 分组和排序 +使用GROUP BY在对数据进行分组后,输出的组的顺序通常是按从小到到大,从A到Z升序输出的,但是SQL规范并没有对此进行明确要求,所以有可能不是顺序的,可以使用ORDER BY来对分组进行升序或者降序排序。 +![image.png](../static/12609483-31c0f328654affff.png) + +#### SELECT子句顺序 +在使用这些语句时,它们的先后顺序应该要按下面的表中顺序来写 +![image.png](../static/12609483-6576136fd07e6686.png) + +![image.png](../static/12609483-55a1e1bbfe1627f6.png) + +### 第十四章 使用子查询 + +#### 子查询作为WHERE子句的条件 + +有时候一条SELECT语句无法满足我们的需求,我们可以把一条SELECT语句的结果用于另外一条SELECT语句的WHERE子句,来实现复杂查询。 + +例如:我们想要获取订购物品TNT2的所有客户的名字和联系方式: +可以按照下图中的复杂查询实现: + +(1) 查询包含物品TNT2的所有订单的编号。 +(2) 根据订单编号查询所有客户的ID。 +(3) 根据客户的ID查询名字和联系方式。 +![image.png](../static/12609483-d2ec43d839dc3f9c.png) + +在WHERE子句中使用子查询能够编写出功能很强并且很灵活的 SQL语句。对于能嵌套的子查询的数目没有限制,不过在实际使用时由于 性能的限制,不能嵌套太多的子查询。 + +需要的注意的地方: + +1.能嵌套的子查询的数目没有限制,不过在实际使用时由于 性能的限制,不能嵌套太多的子查询。 + +2.列必须匹配,在WHERE子句中使用子查询,应该保证SELECT语句具有与WHERE子句中相同数目的列。通常,子查询将返回单个列并且与单个列匹配,但如果需要也可以使用多个列。 + +3.子查询一般与IN操作符结合使用,但也可以用于测试等于(=)、 不等于(<>)等。 + +#### 子查询结果作为计算字段 +例如:我们想要在获取顾客的信息的同时,获取客户的订单数,可以使用子查询来实现,如下图所示: +![image](../static/12609483-f60146d5eaffeba0.png) +当然这个需求也可以使用JOIN来实现 + +### 第十五章 连接表 +有时候针对单表的查询无法满足我们的需求,我们需要连接多个表,返回一组输出。连接并不是物理实体,只是在查询时建立。 + +#### 笛卡尔积 +在进行连接查询时,如果不指定任何WHERE 条件,那么返回的结果会是笛卡尔积,会拿第一个表中的行数与第二个表中的所有行进行配对,最终总行数会是第一个表的行数乘以第二个表中的行数。 + +![image-20190627195354062.png](../static/12609483-1200a07007edb639.png) + +#### WHERE条件 + +如果指定了WHERE条件,得到的结果会是根据条件对笛卡尔积的结果进行筛选过滤后的结果。例如在这个例子中,指定了products表的vend_id与vendors表的vend_id相等作为筛选条件,这样,连接的结果就是拿vendors表的vend_id去products表中找相匹配的数据。 + +#### 等值连接(内连接) + +![image-20190627195542156.png](../static/12609483-232a5ad60979603b.png) + +上面的这种连接其实是等值连接,可以用连接的语法来写,可以更加明确连接类型 +![image.png](../static/12609483-e831050cd046fede.png) + +#### 连接多个表 + +一条SELECT语句可以连接的表的数量没有限制,可以连接多个表,进行查询 +![image.png](../static/12609483-bbdf1cc5ac1a4ef0.png) + + +#### 使用连接来替代子查询 + +之前通过子查询嵌套来完成多表查询,现在可以使用连接来实现 + +![image.png](../static/12609483-c41c27cd14c59c4d.png) + + +### 第十六章 创建高级联结 + +本章将讲解外连接,以及如何对被联结的表使用表别名和聚集函数。 + +#### 使用表别名 +除了可以对列,计算字段起别名以外,还可以对表起别名。主要有以下好处: + +1.缩短SQL语句(有些表名太长,可以起短的别名) + +2.允许在单条SELECT语句中多次使用相同的表(对表进行自连接查询时会需要多次使用相同的表,在下面有相应的例子说明) + +除了上一章讲到的内部连接(等值连接)以为,还有自连接,自然连接,外部连接三种连接: +#### 自连接 +自连接指的是一张表对自身进行连接,进行信息查询。 +例如: +某物品(其ID为DTNTR)存在问题,因此想知道生产该物 品的供应商生产的其他物品是否也存在这些问题。此查询要求首先找到 生产ID为DTNTR的物品的供应商,然后找出这个供应商生产的其他物品。 +可以使用自连接的实现: +![image](../static/12609483-e9a01efc5be2b1b0.png) + +此查询中需要的两个表实际上是相同的表,因此products表在 +FROM子句中出现了两次。虽然这是完全合法的,但对products 的引用具有二义性,所以使用表别名避免歧义。 +当然解决上面的这个查询需求也可以使用子查询来实现,如下图所示: +![image](../static/12609483-2e06d5c0cc55c67b.png) + +#### 外连接 +内部连接会将一个表中的行与另一个表中的行想关联,有时候也需要包含不满足关联条件的那些行,这就是外连接。 +例如: +![image.png](../static/12609483-4af826237ddda349.png) +这条SELECT语句使用了关键字OUTER JOIN来指定联结的类型(而不是在WHERE子句中指 定)。但是,与内部联结关联两个表中的行不同的是,外部联结还包括没 有关联行的行。在使用OUTER JOIN语法时,必须使用RIGHT或LEFT关键字 指定包括其所有行的表(RIGHT指出的是OUTER JOIN右边的表,而LEFT 指出的是OUTER JOIN左边的表)。上面的例子使用LEFT OUTER JOIN从FROM 子句的左边表(customers表)中选择所有行。 + +使用带聚集函数的连接 +聚集函数也可以和连接结合起来使用。 +![image](../static/12609483-7c3063235346d100.png) +在这个例子中,使用INNER JOIN将customers和orders表互相关联。GROUP BY子句按客户分组数据,因此,函数调用COUNT (orders.order_num)对每个客户的订单计数,将它作为num_ord返回。 + +#### 注意事项: +1.注意所使用的联结类型。一般我们使用内部联结,但使用外部联 结也是有效的。 + +2.保证使用正确的联结条件,否则将返回不正确的数据。 + +3.应该总是提供联结条件,否则会得出笛卡儿积。 + +4.在一个联结中可以包含多个表,甚至对于每个联结可以采用不同的联结类型。虽然这样做是合法的,一般也很有用,但应该在一起测试它们前,分别测试每个联结。这将使故障排除更为简单。 + +### 第十七章 组合查询 +在 MySQL 中,可以执行多条查询语句,然后对多个结果集,使用UNION语句合并成单个查询结果集返回。主要有以下两种应用场景: +1.在单个查询中从不同的表返回类似结构的数据。 +2.对单个表执行多个查询,将结果集合并成一个结果集。 + +#### 使用UNION +例如我们需要价格小于等于5的所有物品的一个列表,而且 还想包括供应商1001和1002生产的所有物品(不考虑价格), +![image](../static/12609483-c73d7b850397558a.png) + +这条语句由前面的两条SELECT语句组成,语句中用UNION关键 +字分隔。UNION指示MySQL执行两条SELECT语句,并把输出组 合成单个查询结果集,当然这个需求也可以使用多条WHERE语句来实现。 + +#### 注意事项: +1.UNION必须由两条或两条以上的SELECT语句组成,语句之间用关 键字UNION分隔(因此,如果组合4条SELECT语句,将要使用3个 UNION关键字)。 + +2.UNION中的每个查询必须包含相同的列、表达式或聚集函数(不过 +3.列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以 +隐含地转换的类型(例如,不同的数值类型或不同的日期类型)。 如果遵守了这些基本规则或限制,则可以将并用于任何数据检索任务。 + +4.在用UNION组合查询时,如果需要对结果进行排序,只能使用一条ORDER BY子句,它必须出现在最后一条SELECT语句之后。对 于结果集,不存在用一种方式排序一部分,而又用另一种方式排序另一 部分的情况,因此不允许使用多条ORDER BY子句。 + +5.UNION从查询结果集中自动去除了重复的行(换句话说,它的行为与 单条SELECT语句中使用多个WHERE子句条件一样)。因为供应商1002生产 的一种物品的价格也低于5,所以两条SELECT语句都返回该行。在使用 UNION时,重复的行被自动取消。这是UNION的默认行为,但是如果允许重复,可以使用UNION ALL而不是UNION。如下图所示: +![image.png](../static/12609483-6de69717b1e77bc6.png) + +### 第十八章 组合查询 +当我们需要对文本进行匹配,可以使用LIKE+通配符,或正则表达式的方式来实现,但是这样会存在很多限制: + +1.性能不高——通配符和正则表达式匹配通常要求MySQL尝试匹配表中所有行(而且这些搜索极少使用表索引)。因此,由于被搜索行数不断增加,这些搜索可能非常耗时。 + +2.不太灵活——使用通配符和正则表达式匹配,很难(而且并不总是能)明确地控制匹配什么和不匹配什么。例如,指定一个词必须匹配,一个词必须不匹配,而一个词仅在第一个词确实匹配的情况下才可以匹配或者才可以不匹配。 + +3.无法智能化——虽然基于通配符和正则表达式的搜索提供了非常灵活的搜索,但它们都不能提供一种智能化的选择结果的方法。 例如,一个特殊词的搜索将会返回包含该词的所有行,而不区分包含单个匹配的行和包含多个匹配的行(按照可能是更好的匹配 来排列它们)。类似,一个特殊词的搜索将不会找出不包含该词但包含其他相关词的行。所以就有了全文搜索,为了进行全文本搜索,必须索引被搜索的列,而且要随着数据的改 变不断地重新索引。在对表列进行适当设计后,MySQL会自动进行所有 的索引和重新索引。在索引之后,SELECT可与Match()和Against()一起使用以实际执行 搜索。 + +#### 启用全文本搜索支持 +在建表时或者建表以后使用FULLTEXT语句指定全文搜索的列,MySQL根据子句FULLTEXT(note_text)的指示对它进行索引,在之后该列增加、更新或删除行时, 索引随之自动更新 +如下图所示: +![image.png](../static/12609483-529e2cf20c540145.png) + +#### 注意事项: +如果正在导入数据到一个新表, 此时不应该启用FULLTEXT索引。应该首先导入所有数据,然后再修改表,定义FULLTEXT,这样花费的时间会更少。 + +#### 进行全文本搜索 +在索引之后,使用两个函数Match()和Against()执行全文本搜索, +Match() 指定被搜索的列 +Against() 指定要使用的搜索表达式 +如下图所示:SELECT语句检索单个列note_text,将包含rabbit的行进行返回。(全文搜索默认不区分大小写,除非使用BINARY语句进行修饰) +![image.png](../static/12609483-69f72d04b83e18d4.png) +![image.png](../static/12609483-13b966cad4481566.png) + +全文搜索还可以使用Rank对结果进行排序,Match()和Against() 用来建立一个计算列(别名为rank),此列包含全文本搜索计算出的等级 值。等级由MySQL根据行中词的数目、唯一词的数目、整个索引中词的 总数以及包含该词的行的数目计算出来。不包含搜索词的行等级为0(因此不被前一例子中的WHERE子句选择)。确实包含搜索词的两个行每行都有一个等级值,文本中词靠前的行的等级值比词靠后的行的等级值高。如下图所示: +![image.png](../static/12609483-6d1abd9d8ecbe769.png) +![image.png](../static/12609483-2650e43d71ae45e3.png) + +如果指定多个搜索项,则包含多数匹配词的 那些行将具有比包含较少词(或仅有一个匹配)的那些行高的 等级值。 + +#### 使用查询扩展 + +查询扩展用来设法放宽所返回的全文本搜索结果的范围,它可以先进行一个基本的全文本搜索,找出与搜索条件匹配的所有行。其次,MySQL检查这些匹配行并选择所有有用的词,再其次,MySQL再次进行全文本搜索,这次不仅使用原来的条件, 而且还使用所有有用的词。如下图所示; +![image.png](../static/12609483-810b67dd5e9b46e9.png) +![image.png](../static/12609483-dc91fefa6c8067c2.png) + +#### 布尔文本搜索 +MySQL支持全文本搜索的另外一种形式,称为布尔方式(boolean +mode)。可以指定要匹配的词,要排斥的词,排列提示(指定某些词比其他词更重要,更重要的词等级更高),表达式分组等。即使没有定义 FULLTEXT索引,也可以使用它。但这是一种非常缓慢的操作。 +例如: +在下图里面的查询中,会匹配词heavy,但-rope*明确地 +分析指示MySQL排除包含rope*(任何以rope开始的词,包括 ropes)的行。 +![image.png](../static/12609483-231d3ec0b1d24103.png) + +除了布尔操作符-和*,-排除一个词,而* 是截断操作符(可想象为用于词尾的一个通配符)。还有以下全文本布尔操作符: +![image.png](../static/12609483-ad60c82070093caf.png) + +下面是一些全文本布尔操作符使用案例: +![image.png](../static/12609483-178537093cafab1a.png) + +全文本搜索的使用说明 + +1.在索引全文本数据时,短词被忽略且从索引中排除。短词定义为 那些具有3个或3个以下字符的词(如果需要,这个数目可以更改)。 + +2.MySQL带有一个内建的非用词(stopword)列表,这些词在索引全文本数据时总是被忽略。如果需要,可以覆盖这个列表(请参阅MySQL文档以了解如何完成此工作)。 + +3.许多词出现的频率很高,搜索它们没有用处(返回太多的结果)。因此,MySQL规定了一条50%规则,如果一个词出现在50%以上的行中,则将它作为一个非用词忽略。50%规则不用于IN BOOLEAN MODE。 + +4.如果表中的行数少于3行,则全文本搜索不返回结果(因为每个词或者不出现,或者至少出现在50%的行中)。 + +5.忽略词中的单引号。例如,don't索引为dont。 + +6.不具有词分隔符(包括日语和汉语)的语言不能恰当地返回全文 +本搜索结果。 + +7.如前所述,仅在MyISAM数据库引擎中支持全文本搜索。 + +8.没有邻近操作符,邻近搜索是许多全文本搜索支持的一个特 性,它能搜索相邻的词(在相同的句子中、相同的段落中或者 在特定数目的词的部分中,等等。MySQL全文本搜索现在还不支持邻近操作符。 + + +### 第十九章 插入数据 +使用INSERT语句插入数据,大家都很熟悉。 +例如向 Customers 表插入一条name为tom,age为29的数据 +一般有以下两种方式: +``` +INSERT INTO Customers VALUES("tom", "29"); + +INSERT INTO Customers(name, age) VALUES("tom", "29"); +``` +一般推荐第二张方式,因为第一种方式的数据顺序必须与列在表中的数据保持一致,容易写错,其次是当表结构发生改变时,第一种方式需要变更数据顺序,第二种方式不需要。 + +#### 插入检索出的数据 +INSERT一般用来给表插入一个指定列值的行。但是,INSERT还存在 另一种形式,可以利用它将一条SELECT语句的结果插入表中。如下图所示,这个例子使用INSERT SELECT从custnew表中将所有数据导入customers表 +![image.png](../static/12609483-cd0ab58840e43364.png) +![image.png](../static/12609483-5593c6a8abdc9c5d.png) + + +### 第二十章 更新和删除数据 +#### 更新数据 +使用UPDATE语句更新数据,大家都很熟练了,一般UPDATE语句组成部分如下: +``` +UPDATE 表名 +SET 列名 = 新值 +WHERE 过滤条件; +``` +如下图所示: +![image](../static/12609483-1134d72ac589b510.png) +需要注意的是: + +1.在使用UPDATE语句时,不要省略WHERE子句 ,否则就会更新表中所有行。 + +2.IGNORE关键字,如果用UPDATE语句更新多行,并且在更新这些行中的一行或多行时出一个现错误,则整个UPDATE操作被取消 (错误发生前更新的所有行被恢复到它们原来的值)。为即使是发生错误,也继续进行更新,可使用IGNORE关键字,如下所示: UPDATE IGNORE customers... + +#### 删除数据 +使用DELETE语句更新数据,大家也都很熟练了,一般DELETE语句组成部分如下: + +DELETE FROM 表名 +WHERE 过滤条件; + +如下图所示: +![image.png](../static/12609483-d66c9d1ce490a96c.png) +需要注意的是: + +1.在使用DELETE语句时,不要省略DELETE子句 ,否则会删除表中所有行。 + +2.DELETE语句从表中删除行,甚至是删除表中所有行。但是,DELETE不删除表本身。 + +3.如果想从表中删除所有行,不要使用DELETE。 可使用TRUNCATE TABLE语句,它完成相同的工作,但速度更快,因为TRUNCATE实际是删除原来的表并重新创建一个表,而不是逐行删除表中的数据 + +更新和删除的指导原则 + +1.除非确实打算更新和删除每一行,否则绝对不要使用不带WHERE 子句的UPDATE或DELETE语句。 + +2.保证每个表都有主键(如果忘记这个内容,请参阅第15章),尽可能 像WHERE子句那样使用它(可以指定各主键、多个值或值的范围)。 + +3.在对UPDATE或DELETE语句使用WHERE子句前,应该先用SELECT进行测试,保证它过滤的是正确的记录,以防编写的WHERE子句不正确。 + +4.使用强制实施引用完整性的数据库(关于这个内容,请参阅第15 +章),这样MySQL将不允许删除具有与其他表相关联的数据的行。 + +5.MySQL没有撤销(undo)按钮。应该非常小心地使用UPDATE和DELETE,否则你会发现自己更新或删除了错误的数据。 + +### 第二十一章 创建和操纵表 +#### 创建表 +使用CREATE语句来创建一个表,大家都很熟悉了,如下图所示 +![image.png](../static/12609483-16cd9cf2320a5c0b.png) + +需要注意的有以下几点: + +1.在建表时,每一列要么是可为NULL列,要么是NOT NULL列,如果不指定,默认为可为NULL列。 + +2.主键必须保证唯一,不能为NULL。如果使用一个列作为主键,值必须唯一,如果使用多个列作为主键,那么多个列组合的值必须唯一。 + +3.MySQL有一个具体管理和处理数据的内部引擎,在执行SQL语句时,可以使用ENGINE语句指定引擎,如果省略ENGINE=语句,则使用默认引擎(很可能是MyISAM),以下为MySQL常见的几个引擎: +InnoDB + +是一个可 靠的事 务 处 理 引 擎 ( 参 见 第 26 章 ), 它 不 支 持 全 文 本搜索; + +MEMORY + +在功能等同于MyISAM,但由于数据存储在内存(不是磁盘) 中,速度很快,所以特别适合于临时表; + +MyISAM + +是一个性能极高的引擎,它支持全文本搜索(参见第18章), 但不支持事务处理。 + +#### 更新表 +在表建立以后,如果需要对表结构进行修改,我们可以使用ALTER TABLE语句对表进行修改。例如: +![image.png](../static/12609483-a02d8ee4216a48af.png) + + +复杂的表结构更改一般需要手动删除过程,它涉及以下步骤: + +1.用新的列布局创建一个新表。 + +2.使用INSERT SELECT语句从旧表复制数据到新表。如果有必要,可使用转换函数和计算字段。 + +3.检验包含所需数据的新表。 + +4.重命名旧表(如果确定,可以删除它)。 + +5.用旧表原来的名字重命名新表。 + +6.根据需要,重新创建触发器、存储过程、索引和外键。 + +#### 删除表 +删除表(删除整个表而不是其内容)非常简单,使用DROP TABLE语,例如: +删除customers2表 +``` +DROP TABLE customers2; +``` +#### 重命名表 +使用RENAME TABLE语句可以重命名一个表。 +例如: +将表customers2名字改为customers +``` +RENAME TABLE customers2 to customers; +``` +### 第二十二章 使用视图 +视图为虚拟的表。它们包含的不是数据而是根据需要检索数据的查 询。视图提供了一种MySQL的SELECT语句层次的封装,可用来简化数据 处理以及重新格式化基础数据或保护基础数据。 + +![image.png](../static/12609483-bd74b09698d89898.png) +后面就可以把productcustomers视图看成一个虚拟表进行查询,如下图所示: +![image.png](../static/12609483-847d713416f5fdff.png) + +#### 视图主要的用途: + +1.重用SQL语句。 + +2.简化复杂的SQL操作。在编写查询后,可以方便地重用它而不必 知道它的基本查询细节。 + +3.使用表的组成部分而不是整个表。 + +4.保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限。 + +5.更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据。 + +#### 视图的规则和限制: + +1.与表一样,视图必须唯一命名(不能给视图取与别的视图或表相 同的名字)。 + +2.对于可以创建的视图数目没有限制。 + +3.为了创建视图,必须具有足够的访问权限。这些限制通常由数据库管理人员授予。 + +4.视图可以嵌套,即可以利用从其他视图中检索数据的查询来构造一个视图。 + +5.ORDER BY可以用在视图中,但如果从该视图检索数据SELECT中也含有ORDER BY,那么该视图中的ORDER BY将被覆盖。 + +6.视图不能索引,也不能有关联的触发器或默认值。 + +7.视图可以和表一起使用。例如,编写一条联结表和视图的SELECT语句。 + +#### 常见的视图操作语句 + +1.视图用CREATE VIEW语句来创建。 + +2.使用SHOW CREATE VIEW viewname;来查看创建视图的语句。 + +3.用DROP删除视图,其语法为DROP VIEW viewname;。 + +4.更新视图时,可以先用DROP再用CREATE,也可以直接用CREATE OR REPLACE VIEW。如果要更新的视图不存在,则第2条更新语句会创建一个视图;如果要更新的视图存在,则第2条更新语句会替换原有视图。 + +我们上面的例子中视图的作用其实是简化复杂SQL的使用,其实视图还有其他的作用,例如: + +#### 用视图重新格式化检索出的数据 + +![image.png](../static/12609483-62986b21673088de.png) + +![image.png](../static/12609483-3403c7d1aac4d4d1.png) + +#### 用视图过滤不想要的数据 +![image.png](../static/12609483-69338fc49a4509a6.png) + +![image.png](../static/12609483-6f4a20afd0628edf.png) + +#### 更新视图 +视图是可更新的(也就是可以对它们使用INSERT、UPDATE和DELETE)。更新一个视图将更新其基表(可以回忆一下,视图本身没有数据)。如果你对视图增加或删除行,实际上是对其基表增加或删除行。但是当视图定义中有以下操作时,则不能进行视图的更新: + +1.分组(使用GROUP BY和HAVING); 联结; + +2.子查询; + +3.并; + +4.聚集函数(Min()、Count()、Sum()等); + +5.DISTINCT; + +6.导出(计算)列。 + +### 第二十三章 使用存储过程 +存储过程,就是可以一条或多条MySQL语句的组合起来,并且可以加入一些业务逻辑。 + +#### 创建和执行存储过程 +简单的示例: +使用CREATE PROCEDURE语句创建一个存储过程,对一个SELECT语句进行封装,之后可以使用CALL语句来执行这个存储过程。 +![image.png](../static/12609483-1942478235258bac.png) + +![image.png](../static/12609483-cbcfb324cea0f1bb.png) +需要注意的是,因为在存储过程中会包含一些;分隔符,而在命令行实用程序中,使用;字符来作为语句分隔符,为了避免语法错误,可以使用DELIMITER语句来定义一个新的语句结束分隔符。如下图所示: +![image.png](../static/12609483-2353b802cd0c8d1a.png) + +#### 删除存储过程 + 可以使用DROP PROCEDURE 语句来删除一个存储过程,例如: +删除名为productpricing的存储过程 +``` +DROP PROCEDURE productpricing; +``` +#### 使用参数 +在创建存储过程时,可以使用IN语句来存储传入参数,OUT语句来存储返回结果。 +在下面这个例子中,20005是传入参数,@total是返回结果。传入参数和返回结果也可以定义多个。 +![image.png](../static/12609483-67fdc1962265c48c.png) +![image.png](../static/12609483-684e68614fb23645-9624353-9624370-9624377-9624398-9624404.png) +![,](../static/12609483-05ffe2378a7a63eb.png) + +#### 建立智能存储过程 +在创建存储过程中,也可以使用IF,THEN,END IF语句来设置判断条件,这是存储过程与简单的语句封装最大的区别。 + +例如: +![image.png](../static/12609483-f63c9314fc23399e.png) +![image.png](../static/12609483-92df97def8212433.png) + +![image.png](../static/12609483-2b5a351a9cea481f.png) +添加了另外一个 参数taxable,它是一个布尔值(如果要增加税则为真,否则为假)。在 存储过程体中,用DECLARE语句定义了两个局部变量。DECLARE要求指定 变量名和数据类型,它也支持可选的默认值(这个例子中的taxrate的默 认被设置为6%)。SELECT语句已经改变,因此其结果存储到total(局部 变量)而不是ototal。IF语句检查taxable是否为真,如果为真,则用另 一SELECT语句增加营业税到局部变量total。最后,用另一SELECT语句将 total(它增加或许不增加营业税)保存到ototal。 +BOOLEAN值指定为1表示真,指定为0表示假(实际上,非零值 都考虑为真,只有0被视为假)。通过给中间的参数指定0或1,可以有条件地将营业税加到订单合计上。 + +#### 检查存储过程 +可以使用 SHOW CREATE PROCEDURE 语句显示用来创建一个存储过程的 CREATE 语句也可以使用 SHOW PROCEDURE STATUS 列出所有存储过程。为限制其输出,可使用LIKE指定一个过滤模式,例如:SHOW PROCEDURE STATUS LIKE 'ordertotal'; + +### 第二十四章 使用游标 +游标(cursor)是一个存储在MySQL服务器上的数据库查询, 它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游 标之后,应用程序可以根据需要滚动或浏览结果集中的数据。 + +#### 创建游标 + +定义了一个名为ordernumbers的游标 +![image.png](../static/12609483-37c40b2102434890.png) + +#### 打开和关闭游标 +打开名为ordernumbers的游标 +``` +OPEN ordernumbers; +``` +关闭名为ordernumbers的游标 +``` +CLOSE ordernumbers; +``` +如果不明确关闭游标,MySQL将会在到达END语句时自动关闭它。 + +#### 使用游标数据 +![image.png](../static/12609483-fc2ea16adcffe4b5.png) +![image.png](../static/12609483-ab790e64ff6b6e17.png) +这个例子使用FETCH检索当前order_num到声明的名为o的变量中。但与前一个例子不一样的是,这个 例子中的FETCH是在REPEAT内,因此它反复执行直到done为真(由UNTIL done END REPEAT;规定)。为使它起作用,用一个DEFAULT 0(假,不结 束)定义变量done。当在 FETCH 语句中引用的游标位置处于结果表最后一行之后时,SQLSTATE会为02000,这个时候done会为真,停止循环。 + +### 第二十五章 使用触发器 +可以使用触发器是在MySQL响应 INSERT UPDATE DELETE 语句前后自动执行一条MySQL语句。 + +#### 创建触发器 +创建触发器语句的格式一般是 +CREATE TRIGGER 触发器名称 触发时机 触发操作 FOR EACH ROW 执行操作; +例如: +``` +CREATE TRIGGER newproduct AFTER INSERT FOR EACH ROW SELECT 'Product added'; +``` +CREATE TRIGGER用来创建名为newproduct的新触发器。触发器 +可在一个操作发生之前或之后执行,这里给出了AFTER INSERT, 所以此触发器将在INSERT语句成功执行后执行。这个触发器还指定FOR EACH ROW,因此代码对每个插入行执行。在这个例子中,文本Product added将对每个插入的行显示一次。 + +#### 删除触发器 +``` +DROP TRIGGER newproduct;删除名为newproduct的触发器 +``` +#### INSERT触发器 + +1. 在INSERT触发器代码内,可引用一个名为NEW的虚拟表,访问被 插入的行; + +2. 在BEFORE INSERT触发器中,NEW中的值也可以被更新(允许更改 被插入的值); + +3. 对于AUTO_INCREMENT列,NEW在INSERT执行之前列的值会是0,在INSERT 执行之后包含新的自动生成值。 +![image.png](../static/12609483-c50ea2a774c79417.png) +![image.png](../static/12609483-a2cd8a6bb15c770c.png) +上面的例子中创建一个名为neworder的触发器,它按照AFTER INSERT ON orders执行。在插入一个新订单到orders表时,MySQL生 成一个新订单号并保存到order_num中。触发器从NEW. order_num取得这个值并返回它。 + +#### DELETE触发器 + +DELETE触发器在DELETE语句执行之前或之后执行,在DELETE触发器代码内,你可以引用一个名为OLD的虚拟表,访问被删除的行。OLD中的值全都是只读的,不能更新。 +![image.png](../static/12609483-a0cfc3f69174cec6.png) +上面这个例子中,在任意订单被删除前将执行此触发器。它使用一条INSERT语句将OLD中的值(要被删除的订单)保存到一个名为archive_ orders的存档表中(为实际使用这个例子,你需要用与orders相同的列 创建一个名为archive_orders的表)。 + +#### UPDATE触发器 + +UPDATE触发器在UPDATE语句执行之前或之后执行。在UPDATE触发器代码中,你可以引用一个名为OLD的虚拟表访问 以前(UPDATE语句前)的值,引用一个名为NEW的虚拟表访问新 更新的值。在BEFORE UPDATE触发器中,NEW中的值可能也被更新(允许更改 将要用于UPDATE语句中的值)。OLD中的值全都是只读的,不能更新。 +![image.png](../static/12609483-4c56c2be3b977b77.png) +上面面的例子保证州名缩写总是大写(不管UPDATE语句中给出的是大 写还是小写) + +#### 注意事项: + +1.只有表才支持触发器,视图不支持(临时表也不 支持)。 + +2.如果BEFORE触发器失败,则MySQL将不执行请求的操作。此外,如果BEFORE触发器或语句本身失败,MySQL 将不执行AFTER触发器(如果有的话)。 + +3.与其他DBMS相比,MySQL 5中支持的触发器相当初级。未来的MySQL版本中有一些改进和增强触发器支持的计划。 + +4.创建触发器可能需要特殊的安全访问权限,但是,触发器的执行是自动的。如果INSERT、UPDATE或DELETE语句能够执行,则相关 的触发器也能执行。 + +5.应该用触发器来保证数据的一致性(大小写、格式等)。在触发器中执行这种类型的处理的优点是它总是进行这种处理,而且是透 明地进行,与客户机应用无关。 + +6.触发器的一种非常有意义的使用是创建审计跟踪。使用触发器, 把更改(如果需要,甚至还有之前和之后的状态)记录到另一个 表非常容易。 + +7.遗憾的是,MySQL触发器中不支持CALL语句。这表示不能从触发 器内调用存储过程。所需的存储过程代码需要复制到触发器内。 + +### 第二十六章 管理事务处理 +事务处理可以用来维护数据库的完整性,它保证一组SQL语句要么完全执行,要么完全不执行。利用事务处理,可以保证一组操作不会中途停止,它们 或者作为整体执行,或者完全不执行(除非明确指示)。如果没有错误发 生,整组语句提交给(写到)数据库表。如果发生错误,则进行回退(撤 销)以恢复数据库到某个已知且安全的状态。 +事务(transaction)指一组SQL语句; +回退(rollback)指撤销指定SQL语句的过程; +提交(commit)指将未存储的SQL语句结果写入数据库表; +保留点(savepoint)指事务处理中设置的临时占位符(place- +holder),你可以对它发布回退(与回退整个事务处理不同)。 + +#### 控制事务处理 +``` +事务开始 START TRANSACTION +``` +#### 使用ROLLBACK +MySQL的ROLLBACK命令用来回退(撤销)MySQL语句。例如: +![image.png](../static/12609483-665f81152013e93c.png) +![image.png](../static/12609483-1048ece433706d22.png) +从显示ordertotals表(此表在第24章中填充)的内 +容开始。首先执行一条SELECT以显示该表不为空。然后开始一 个事务处理,用一条DELETE语句删除ordertotals中的所有行。另一条 SELECT语句验证ordertotals确实为空。这时用一条ROLLBACK语句回退 START TRANSACTION之后的所有语句,最后一条SELECT语句显示该表不为空。 +ROLLBACK只能在一个事务处理内使用(在执行一条START TRANSACTION命令之后)。可以回退INSERT、UPDATE和 DELETE语句。不能回退SELECT,CREATE,DROP语句。 + +#### 使用COMMIT +一般的MySQL语句都是直接针对数据库表执行和编写的。这就是所谓的隐含提交(implicit commit),即提交(写或保存)操作是自动进行的。但是,在事务处理块中,提交不会隐含地进行。为进行明确的提交, 使用COMMIT语句。 + +![image.png](../static/12609483-1867c2cea2e07c0f.png) +在这个例子中,从系统中完全删除订单20010。因为涉及更新 +两个数据库表orders和orderItems,所以使用事务处理块来 保证订单不被部分删除。最后的COMMIT语句仅在不出错时写出更改。如 果第一条DELETE起作用,但第二条失败,则DELETE不会提交(会被自动撤销)。 + +#### 使用保留点 +简单的ROLLBACK和COMMIT语句就可以写入或撤销整个事务处理。但 是,只是对简单的事务处理才能这样做,更复杂的事务处理可能需要部 分提交或回退。为了支持回退部分事务处理,必须能在事务处理块中合适的位置放 置占位符。这样,如果需要回退,可以回退到某个占位符。 + +#### 创建占位符 +``` +SAVEPOINT delete1;创建一个名称为delete1的占位符 +ROLLBACK TO delete1;回退到delete1的占位符 +``` +保留点在事务处理完成(执行一条ROLLBACK或 COMMIT)后自动释放。自MySQL 5以来,也可以用RELEASE SAVEPOINT明确地释放保留点。 + +#### 更改默认的提交行为 +默认的MySQL行为是自动提交所有更改。换句话说,任何 时候你执行一条MySQL语句,该语句实际上都是针对表执行的,而且所做 的更改立即生效。为指示MySQL不自动提交更改,可以使用 +``` +SET autocommit=0; +``` +autocommit标志决定是否自动提交更改,不管有没有COMMIT +语句。设置autocommit为0(假)指示MySQL不自动提交更改 (直到autocommit被设置为真为止)。 + +### 第二十七章 全球化和本地化 +数据库表被用来存储和检索数据。不同的语言和字符集需要以不同 的方式存储和检索。因此,MySQL需要适应不同的字符集(不同的字母 和字符),适应不同的排序和检索数据的方法。 + +#### 使用字符集和校对顺序 +``` +show CHARACTER SET; +``` +可以展示可用的字符集,MySQL 默认字符集是latin1,一般我们常用的就是utf8 +``` +show COLLATION; +``` +可以展示所支持校对以及它们适用的字符集的完整列表,有的字符集具有不止一种校对。 + +通常系统管理在安装时定义一个默认的字符集和校对。此外,也可 以在创建数据库时,指定默认的字符集和校对。和校对,可以使用以下语句进行查看: +``` +show VARIABLES like 'character%';查看字符集相关的配置 +show VARIABLES like 'collation%';查看校对相关的配置 +``` +#### 给表指定字符集和校对 +![image.png](../static/12609483-a7bf91e24890bdf5.png) +这个例子中指定了CHARACTER SET和COLLATE两者。一般,MySQL如 +下确定使用什么样的字符集和校对。 + +1.如果指定CHARACTER SET和COLLATE两者,则使用这些值。 + +2.如果只指定CHARACTER SET,则使用此字符集及其默认的校对(如SHOW CHARACTER SET的结果中所示)。 + +3.如果既不指定CHARACTER SET,也不指定COLLATE,则使用数据库默认。 + +#### 对列指定字符集和校对 +![image.png](../static/12609483-008d6304d53ba2a2.png) + +#### 查询时指定校对顺序 +![image.png](../static/12609483-d36a73cde36c31ec.png) +此SELECT使用COLLATE指定一个备用的校对顺序(在这个例子 中,为区分大小写的校对)。除了这里看到的在ORDERBY子 句中使用以外,COLLATE还可以用于GROUP BY、HAVING、聚集 函数、别名等。 + +### 第二十八章 安全管理 +MySQL用户账号和信息存储在名为mysql的MySQL数据库中。 +获得所有用户账号列表 +![image.png](../static/12609483-6305ecdf2f8995f9.png) + +![image.png](../static/12609483-1dbb238efbfcfdfc.png) + +#### 创建用户账号 +CREATE USER ben IDENTIFIED BY 'passwOrd'; +CREATE USER创建一个新用户账号。在创建用户账号时不一定需要密码,不过这个例子用IDENTIFIED BY 'passwOrd'给出了一个密码。 + +#### 重新命名一个用户账号 +``` +RENAME USER ben TO bforta; +``` + +#### 删除用户账号 +``` +DROP USER bforta; +``` + +#### 设置访问权限 +为看到赋予用户账号的权限,使用SHOW GRANTS FOR,如下图所示: +![image.png](../static/12609483-f62b69326ef4844d.png) +输出结果显示用户bforta有一个权限USAGE ON *.*。此结果表示在任意数据库和任意表上对任何数据没有权限。 +用户定义为user@host MySQL的权限将会把用户名和主机名结合定义。如果不指定主机名,则使用默认的主机名%(授予用户访问权限而不管主机名)。 + +#### GRANT语句 +GRANT语句的一般格式是 +GRANT 权限 ON 范围 TO 用户; +例如: +``` +GRANT SELECT ON crashhouse.* TO bforta; +``` +GRANT允许用户在crashcourse.*(crashcourse数据库的所 +有表)上使用SELECT语句。 + +SHOW GRANTS可以用来显示bforta用户的权限 +![image.png](../static/12609483-05e1a6bce1c29051.png) + +#### REVOKE +GRANT的反操作为REVOKE,用它来撤销特定的权限。 +``` +REVOKE SELECT ON crashhouse.* FROM bforta; +``` +这条REVOKE语句取消刚赋予用户bforta的SELECT访问权限。被 撤销的访问权限必须存在,否则会出错。 + +GRANT和REVOKE可在几个层次上控制访问权限: + +1.整个服务器,使用GRANT ALL和REVOKE ALL; + +2.整个数据库,使用ON database.*; + +3.特定的表,使用ON database.table; + +4.特定的列; + +5.特定的存储过程。 + +下面是可以授予或撤销的每个权限: +![image.png](../static/12609483-199bd1a12bee5cc3.png) + +![image.png](../static/12609483-66b5c2d55ba4d32a.png) +#### 注意事项: +#### 提前授权 +在使用GRANT和REVOKE时,用户账号必须存在, 但对所涉及的对象没有这个要求。这允许管理员在创建数据库 和表之前设计和实现安全措施。这样做的副作用是,当某个数据库或表被删除时(用DROP语 句),相关的访问权限仍然存在。而且,如果将来重新创建该 数据库或表,这些权限仍然起作用。 + +#### 更改密码 +更改特定用户的密码 +``` +SET PASSWORD FOR bforta = Password('123456'); +``` +更改当前用户的密码 +``` +SET PASSWORD = Password('123456'); +``` +### 第二十九章 数据库维护 +备份数据一般有以下几种方案: + +1.使用命令行实用程序 mysqldump 转储所有数据库内容到某个外部 文件。在进行常规备份前这个实用程序应该正常运行,以便能正 确地备份转储文件。 + +2.可用命令行实用程序 mysqlhotcopy 从一个数据库复制所有数据 (并非所有数据库引擎都支持这个实用程序)。 + +3.可以使用 MySQL 的 BACKUP TABLE 或 SELECT INTO OUTFILE 转储所有数据到某个外部文件。这两条语句都接受将要创建的系统文件名,此系统文件必须不存在,否则会出错。数据可以用 RESTORE TABLE 来复原。 + +#### 数据库维护 +ANALYZE TABLE +用来检查表键是否正确,返回的状态信息如下: +![image.png](../static/12609483-c4adf66192e77225.png) + +CHECK TABLE +用来针对许多问题对表进行检查。在MyISAM表上还对索引进行检查。 +CHECK TABLE支持一系列的检查选项(仅用于MyISAM表) : + +CHANGED +检查自最后一次检查以来改动过的表 + +EXTENDED +执行最彻底的检查 + +FAST +只检查未正常关闭的表 + +MEDIUM +检查所有被删 除的链接并进行键检验 + +QUICK +只进行快速扫描 +如下所示,CHECK TABLE发现和修复问题: +![image.png](../static/12609483-f12252aa0fbd3724.png) + +如果MyISAM表访问产生不正确和不一致的结果,可能需要用REPAIR TABLE来修复相应的表。这条语句不应该经常使用,如果需要经常使用,可能会有更大的问题要解决。 +如果从一个表中删除大量数据,应该使用OPTIMIZE TABLE来收回所用的空间,从而优化表的性能。 + +#### 诊断启动问题 +服务器启动问题通常在对MySQL配置或服务器本身进行更改时出现。MySQL在这个问题发生时报告错误,但由于多数MySQL服务器是作为系统进程或服务自动启动的,这些消息可能看不到。 +在排除系统启动问题时,首先应该尽量用手动启动服务器。MySQL 服务器自身通过在命令行上执行mysqld启动。下面是几个重要的mysqld命令行选项: + +--help +显示帮助 + +--safe-mode +装载减去某些最佳配置的服务器 + +--verbose +显示全文本消息(为获得更详细的帮助消息与--help联合使用) + +--version +显示版本信息然后退出 + +查看日志文件 + +MySQL维护管理员依赖的一系列日志文件。主要的日志文件有以下几种。 + +错误日志 + +它包含启动和关闭问题以及任意关键错误的细节。此日志通常名为hostname.err,位于data目录中。此日志名可用 --log-error命令行选项更改。 + +查询日志 + +它记录所有MySQL活动,在诊断问题时非常有用。此日志文件可能会很快地变得非常大,因此不应该长期使用它。此 日志通常名为hostname.log,位于data目录中。此名字可以用 --log命令行选项更改。 + +二进制日志 + +它记录更新过数据(或者可能更新过数据)的所有语句。此日志通常名为hostname-bin,位于data目录内。此名字 可以用--log-bin命令行选项更改。注意,这个日志文件是MySQL 5 中添加的,以前的MySQL版本中使用的是更新日志。 + +缓慢查询日志 + +顾名思义,此日志记录执行缓慢的任何查询。这 个日志在确定数据库何处需要优化很有用。此日志通常名为 hostname-slow.log,位于data目录中。此名字可以用 +--log-slow-queries命令行选项更改。 在使用日志时,可用FLUSH LOGS语句来刷新和重新开始所有日志文件。 + +### 第三十章 改善性能 +#### 改善性能的一些方法: + +1.首先,MySQL(与所有DBMS一样)具有特定的硬件建议。在学习和研究MySQL时,使用任何旧的计算机作为服务器都可以。但对用于生产的服务器来说,应该坚持遵循这些硬件建议。 + +2.一般来说,关键的生产DBMS应该运行在自己的专用服务器上。 + +3.MySQL是用一系列的默认设置预先配置的,从这些设置开始通常是很好的。但过一段时间后你可能需要调整内存分配、缓冲区大 小等。(为查看当前设置,可使用SHOW VARIABLES;和SHOW STATUS;) + +4.MySQL一个多用户多线程的DBMS,换言之,它经常同时执行多个任务。如果这些任务中的某一个执行缓慢,则所有请求都会执 行缓慢。如果你遇到显著的性能不良,可使用SHOW PROCESSLIST 显示所有活动进程(以及它们的线程ID和执行时间)。你还可以用KILL命令终结某个特定的进程(使用这个命令需要作为管理员登录)。 + +5.总是有不止一种方法编写同一条SELECT语句。应该试验联结、并、子查询等,找出最佳的方法。 + +6.使用EXPLAIN语句让MySQL解释它将如何执行一条SELECT语句。 + +7.一般来说,存储过程执行得比一条一条地执行其中的各条MySQL语句更快 + +8.应该总是使用正确的数据类型。 + +9.决不要检索比需求还要多的数据。换言之,不要用SELECT *(除非你真正需要每个列)。 + +10.有的操作(包括INSERT)支持一个可选的DELAYED关键字,如果使用它,将把控制立即返回给调用程序,并且一旦有可能就实际执行该操作。 + +11.在导入数据时,应该关闭自动提交。你可能还想删除索引(包括FULLTEXT索引),然后在导入完成后再重建它们。 + +12.必须索引数据库表以改善数据检索的性能。确定索引什么不是一件微不足道的任务,需要分析使用的SELECT语句以找出重复的 WHERE和ORDER BY子句。如果一个简单的WHERE子句返回结果所花的时间太长,则可以断定其中使用的列(或几个列)就是需要索引的对象。 + +13.你的SELECT语句中有一系列复杂的OR条件吗?通过使用多条SELECT语句和连接它们的UNION语句,你能看到极大的性能改进。 + +14.索引改善数据检索的性能,但损害数据插入、删除和更新的性能。如果你有一些表,它们收集数据且不经常被搜索,则在有必要之前不要索引它们。(索引可根据需要添加和删除。) + +15.LIKE很慢。一般来说,最好是使用FULLTEXT而不是LIKE。 + +16.数据库是不断变化的实体。一组优化良好的表一会儿后可能就面目全非了。由于表的使用和内容的更改,理想的优化和配置也会改变。 + +17.最重要的规则就是,每条规则在某些条件下都会被打破。 + + diff --git a/docs/MySQLNote.md b/docs/MySQLNote.md new file mode 100644 index 0000000..04c8b6b --- /dev/null +++ b/docs/MySQLNote.md @@ -0,0 +1,1432 @@ + +(PS:扫描首页里面的二维码进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +下面是主要是自己看了《高性能MySQL》及一些博客后,找了一些MySQL相关的面试题,通过翻书,查资料写的解答,之后会继续更新和完善这一部分内容。 + +#### [1.一条MySQL更新语句的执行过程是什么样的?](#一条MySQL更新语句的执行过程是什么样的?) + +#### [2.脏页是什么?](#脏页是什么?) + +#### [3.Checkpoint是什么?](#Checkpoint是什么?) + +#### [4.undo log,redo log,bin log是什么?](#undolog,redolog,binlog是什么?) +#### [5.MySQL中的事务是什么?](#MySQL中的事务是什么?) +#### [6.MySQL的隔离级别是怎么样的?](#MySQL的隔离级别是怎么样的?) +#### [7.MVCC的实现原理是怎么样的?](#MVCC的实现原理是怎么样的?) +#### [8.MySQL是怎么解决幻读的问题的?](#MySQL是怎么解决幻读的问题的?) +#### [9.MySQL中有哪些锁?](#MySQL中有哪些锁?) +#### [10.B树是什么?](#B树是什么?) +#### [11.B树与B+树的区别是什么?](#B树与B+树的区别是什么?) +#### [12.索引是什么?](#索引是什么?) +#### [13.字符串索引和数字类型索引的区别?](#字符串索引和数字类型索引的区别?) +#### [14.union和union all的区别是什么?](#union和unionall的区别是什么?) +#### [15.Join的工作流程是怎么样的,怎么进行优化?](#Join的工作流程是怎么样的,怎么进行优化?) +#### [16.聚集索引是什么?](#聚集索引是什么?) +#### [17.联合索引是什么?](#联合索引是什么?) +#### [18.覆盖索引是什么?](#覆盖索引是什么?) +#### [19.哪些情况不要建索引?](#哪些情况不要建索引?) +#### [20.主键,唯一性索引,普通索引的区别是什么?](#主键,唯一性索引,普通索引的区别是什么?) +#### [21.InnoDB和MyISAM的区别是什么?](#InnoDB和MyISAM的区别是什么?) +#### [22.什么是分库分表?](#什么是分库分表?) +#### [23.怎么实现跨库分页查询?](#怎么实现跨库分页查询?) +#### [24.MySQL主从复制的工作流程是什么样的?](#MySQL主从复制的工作流程是什么样的?) +#### [25.char类型与varchar类型的区别?](#char类型与varchar类型的区别) +#### [26.查询数量SELECT Count(*)怎么优化?](#怎么优化数量查询?) +#### [27.如何优化MySQL慢查询?](#如何优化MySQL慢查询?) +#### [28.MySQL的join的实现是怎么样的?](#MySQL的join的实现是怎么样的?) + +### 一条MySQL更新语句的执行过程是什么样的? + +![MySQL更新语句执行过程](../static/updateprocess.png) + +#### 1.连接验证及解析 + +客户端与MySQL Server建立连接,发送语句给MySQL Server,接收到后如果是查询语句会先去查询缓存中看,有的话就直接返回了,(新版本的MySQL已经废除了查询缓存,命中率太低了),如果是缓存没有或者是非查询语句,会创建一个解析树,然后进行优化,(解析器知道语句是要执行什么,会评估使用各种索引的代价,然后去使用索引,以及调节表的连接顺序)然后调用innodb引擎的接口来执行语句。 + +#### 2.写undo log + +innodb 引擎首先开启事务,获得一个事务ID(是一直递增的),根据执行的语句生成一个反向的语句,(如果是INSERT会生成一条DELETE语句,如果UPDATE语句就会生成一个UPDATE成旧数据的语句),用于提交失败后回滚,将这条反向语句写入undo log,得到回滚指针,并且更新这个数据行的回滚指针和事务id。(事务提交后,Undo log并不能立马被删除,而是放入待清理的链表,由purge 线程判断是否有其他事务在使用undo 段中表的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间,简单的说就是看之前的事务是否提交成功,这个事务及之前的事务都提交成功了,这部分undo log才能删除。) + +#### 3.从索引中查找数据 + +根据索引去B+树中找到这一行数据(如果是普通索引,查到不符合条件的索引,会把所有数据查找出来,唯一性索引查到第一个数据就可以了) + +#### 4.更新数据 + +判断数据页是否在内存中? + +#### 4.1数据页在内存中 + +索引是普通索引还是唯一性索引? + +##### 4.1.1普通索引 + +直接更新内存中的数据页 + +##### 4.1.2唯一性索引 + +判断更新后是否会数据冲突(不能破坏索引的唯一性),不会的话就更新内存中的数据页。 + +#### 4.2 数据页不在内存中 + +索引是普通索引还是唯一性索引? + +##### 4.2.1普通索引 + +将对数据页的更新操作记录到change buffer,暂时不更新到磁盘。change buffer会在空闲时异步更新到磁盘。 + +##### 4.2.2 唯一性索引 + +因为需要保证更新后的唯一性,所以不能延迟更新,必须把数据页从磁盘加载到内存,然后判断更新后是否会数据冲突,不会的话就更新数据页。 + +#### 5.写redo log(prepare状态) + +将对数据页的更改写入到redo log,此时redo log中这条事务的状态为prepare状态。 + +#### 6.写bin log(同时将redo log设置为commit状态) + +通知MySQL server已经更新操作写入到redo log 了,随时可以提交,将执行的SQL写入到bin log日志,将redo log 中这条事务的状态改成commit状态,事务提交成功。 + +https://www.cnblogs.com/yuyue2014/p/6121114.html + +##### undo log + +主要是保证事务的原子性,事务执行失败就回滚,用于在事务执行失败后,对数据回滚。 + +是逻辑日志,记录的是SQL语句。 + +在事务提交后,undo log日志不会立即删除,会放到一个待删除的链表中,有purge线程判断是否有其他事务在使用上一个事务之前的版本信息,然后决定是否可以清理,简单的来说就是前面的事务都提交成功了,这些undo才能删除。 + +##### change buffer是什么? + +(change buffer就是将更新数据页的操作缓存下来) + +在更新数据时,如果数据行所在的数据页在内存中,直接更新内存中的数据页。 + +如果不在内存中,为了减少磁盘IO的次数,innodb会将这些更新操作缓存在change buffer中,在下一次查询时需要访问这个数据页时,在执行change buffer中的操作对数据页进行更新。(或者是在MySQL Server空闲时,会将change buffer中所有操作更新到磁盘,也就是俗称的‘刷页’。) + +适合写多读少的场景,因为这样即便立即写了,也不太可能会被访问到,延迟更新可以减少磁盘I/O,只有普通索引会用到,因为唯一性索引,在更新时就需要判断唯一性,所以没有必要。 + +##### redo log + +就是为了保证事务的持久性,在做数据更新操作时,先将对数据页的更改记录到redo log,然后再去更新内存中的数据页,在下次查询数据页或者空闲时间,将操作记录更新到磁盘。这样可以将随机I/O改为顺序I/O。 + +优点是减少磁盘I/O次数,即便发生故障也可以根据redo log来将数据恢复到最新状态。 + +缺点是会造成内存脏页,后台线程会自动对脏页刷盘,或者是淘汰数据页时刷盘,此时会暂时查询操作,影响查询。 + +##### 二段提交制是什么? + +更新时,先改内存中的数据页,将更新操作写入redo log日志,此时redo log进入prepare状态,然后通知MySQL Server执行完了,随时可以提交,MySQL Server将更新的SQL写入bin log,然后调用innodb接口将redo log设置为提交状态,更新完成。 + +如果只是写了bin log就提交,那么忽然发生故障,主节点可以根据redo log恢复数据到最新,但是主从同步时会丢掉这部分更新的数据。 + +如果只是写binlog,然后写redo log,如果忽然发生故障,主节点根据redo log恢复数据时就会丢掉这部分数据。 + +##### 崩溃恢复时的判断规则(以redolog是否commit或者binlog是否完整来确定) + +1. 如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交; +2. 如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并完整: +a. 如果是,则提交事务; +b. 否则,回滚事务。 + +参考资料: + +https://gsmtoday.github.io/2019/02/08/how-update-executes-in-mysql/ + +https://www.infoq.cn/article/M6g1yjZqK6HiTIl_9bex + + +### 脏页是什么? + +就是内存数据页与磁盘内存页的内容不一致时的内存页叫做脏页。内存页数据写入磁盘后,数据一致了,就是干净页了。(其实就是将Change Buffer中缓存的更新写入到磁盘) + +刷脏页的场景: + +1.redo log 写满了,系统会停止所有更新操作,将checkpoint向前推进,将推进这部分日志的脏页更新到磁盘。 + +2.系统内存不够,需要将一部分数据页淘汰,如果是干净页,直接淘汰就行了,脏页的话,需要全部同步到磁盘。 + +3.MySQL自认为空闲时去刷脏页。 + +4.MySQL正常关闭之前,会将脏页刷入磁盘。 + +### Checkpoint是什么? + +就是系统故障后,根据redo来恢复数据时,不需要重做所有日志,只需要重做checkpoint点之后的日志,因为redo log也不能无限大,所以当redo log空间不足时,redo log中那部分被更新到磁盘的日志可以覆盖重用。 + +1、缩短数据库的恢复时间; + +2、缓冲池不够用时,将脏页刷新到磁盘; + +3、重做日志空间不足时,刷新脏页。 + + +### undolog,redolog,binlog是什么? +##### undo log 是什么? + +undo log是一种逻辑日志,是旧数据的备份。有两个作用用于事务回滚和MVCC。 + +执行一条INSERT语句时,会记录一条相反的DELETE语句到日志,执行一条UPDATE语句时,会记录一条相反的UPDATE语句到日志中。 + +##### redo log是什么? + +redo log用于保证数据的持久性。redo log记录的是数据页的物理变化,是物理日志,是新数据的备份,在事务提交前,将redo log 持久化就行,不需要将数据持久化,系统崩溃时,可以根据redo log将数据恢复到最新状态。 + +redo log只做顺序追加操作,当事务需要回滚时,在redo log中也不会删除之前的事务记录。 + +默认是每次事务提交时必须调用fsync操作将redo缓冲区的内容写入磁盘。 + +例如:将A=1修改为A=2,它的流程如下: + +1.事务开始 + +2.将原始数据A=1从磁盘读取到内存,生成反向语句,写入undolog。 + +3.修改A=2, + +4.生成一条redo log 写入到redo log 缓冲区 + +5.调用fsync操作将redo log 缓冲区的内容写入到磁盘,写入binlog + +6.事务提交。 + +##### Bin log 是什么? + +保存的是逻辑日志,主要是存储每一条会修改数据的SQL。 + +https://blog.csdn.net/qq_41652863/article/details/98800650 + + +### MySQL中的事务是什么? + +由一系列数据库操作组成的逻辑过程,可以是一个SQL查询,也可以是一组SQL查询。 + +具备四个特性,也就是acid。 + +**原子性(Atomic)**:一个事务就是最小的工作单元,要么执行成功提交,要么执行失败回滚。 + +**一致性(Consistence)**:就是事务的执行不会影响数据库的完整性,保证事务只能把数据库从一个有效(正确)的状态“转移”到另一个有效(正确)的状态,不能执行完一个事务后,使得数据库不符合数据库定义的规则,例如让某个有唯一性约束的字段存在两个相同的值,破坏了唯一性。 + +**隔离性(isolation)**:就是事务在执行过程中,两个事务之间是隔离的,事务在执行成功之前,所做的修改对其他事务是不可见的。 + +**持久性(durability)**:事务执行成功后,对数据的修改是永久的,即便发生故障重启也不会丢失数据。 + +### MySQL的隔离级别是怎么样的? + +##### 未提交读 + +事务还没有提交的修改,其他事务都可以读取到。可能会有脏读的问题,就是读到一些未提交的脏数据。 + +##### 提交读 + +其他事务提交的修改,事务在执行过程中可以读取到,如果一个事务在执行过程中需要两次读取同一行数据,可能会不一致。一般发生在UPDATE和DELETE操作。(大部分数据库系统是采用的这个,但是mysql不是) + +这个隔离级别下,读是不加锁的,写,更新,删除是加锁的,如果更新的行是可以通过索引查找到,那么是对这些行加行锁,否则会将所有行都加锁,然后返回给MySQL Server,让他来进行过滤,对于不满足条件的行解锁。 + +但是还是会有幻读的问题发生(幻读就是事务A在读取和写入符合的条件的记录时,其他事务又插入了一条符合条件的记录,此时事务A二次读取时会产生幻行,一般发生在INSERT操作。) + +##### 可重复读 + +在事务开始时,记录当时的状态,在第二次读取同一行数据时,除非是本事务做的修改,否则读取的都是事务开始时的数据。可以解决脏读的问题,没法解决幻读的问题。这是MySQL的默认事务隔离级别。(MySQL在可重复读的隔离级别下,通过MVCC机制和Next-key Lock解决了幻读的问题。) + +##### 可串行化 + +强制事务串行执行,会让读取每一行都加锁,读用读锁,写用写锁,读写锁互斥,可以解决幻读的问题。并发比较多的话可能会造成大量的超时等待和锁竞争。如果业务并发的特别少或者没有并发,同时又要求数据及时可靠的话 + +### MVCC的实现原理是怎么样的? + +mvcc主要适用于可重复读,可以解决幻读的问题。 + +innodb在解决幻读的问题主要是通MVVC 多版本并发版本控制来实现的。 + +就是每一行数据中额外保存两个隐藏的列: + +**插入或上次更新该行的事务ID**(删除也被认为是一次更新,只不过在代表删除的更新操作中,行中的特殊位被设置为将其标记为已删除。这个事务ID可以认为是数据行的修改版本号。) + +**回滚指针**(指向undo log中用于事务回滚的日志记录)。 + +具体流程: + +##### 1.插入操作 + +每次开始事务时,会对系统版本号+1作为当前事务的版本号。 + +插入数据后,将事务的版本号作为数据行的创建版本号。 + +##### 2.删除操作 + +在使用SQL语句删除行时,并不会立即将其从数据库中物理删除,只会将其标记为删除,并且修改更新该行的事务ID。(`InnoDB`只会在丢弃为删除而编写的undo log日志记录时,才物理删除相应的行及其索引记录。此删除操作称为[purge](https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_purge),它非常快,通常花费与执行删除操作的SQL语句相同的时间顺序。) + +##### 4.或更新操作 + +将当前的事务版本号作为数据行的更新版本号。 + +##### 5.查询操作 + +数据行要被查询出来必须满足两个条件, + +数据行没有标记为删除或者标记为删除但是删除的事务ID>当前事务ID的数据(否则数据已经被标记删除了) + +更新事务ID<=当前事务ID的数据(否则数据是后面的事务创建出来的,或者是被修改过的,那么需要去undo log中找上次的快照数据。) + +如果查询时,该行数据被加了X锁,那么读数据的事务不会进行等待,而是会根据该行数据中的回滚指针undo log日志中读之前版本的数据(这里存储的数据本身是用于回滚的),在可重复读的隔离级别下,从undo log中读取的数据总是事务开始时的快照数据(也就是版本号小于当前事务id的数据),在提交读的隔离级别下,从undo log中读取的总是最新的快照数据。 + +参考文档: + +https://dev.mysql.com/doc/refman/5.7/en/innodb-multi-versioning.html + +https://blog.csdn.net/qq_41652863/article/details/98800650 + +### MySQL是怎么解决幻读的问题的? + +幻读就是事务执行过程中,在查询一个范围的数据时,有新的数据插入到这个范围,导致两次查询的数据不一致。因为读分为快照读和实时读, + +**快照读** + +我们普通的SELECT语句都是普通读,也就是读取的数据都是事务开始时那个状态的数据,普通读的幻读问题主要是通过MVCC来解决的,具体可以看上面的MVCC中的查询操作。 + +**实时读** + +SELECT *** FOR UPDATE 在查询时会先申请X锁 + +SELECT *** IN SHARE MODE 在查询时会先申请S锁 + +就是实时读,就是读取的是实时的数据,而不快照数据,读的时候会加Next-Key Lock锁住当前的记录,以及左右两个区间的间隙,这样在读的时候就不能往我们的查询范围插入数据了。 + + +### MySQL中有哪些锁? + +#### 全局锁 + +就是对整个数据库加锁,让整个数据库处于只读状态,所有更新操作停止。(如果是主库就不能执行更新语句,从库也不能执行同步过来的bin log) + +最常用的场景是对数据库加锁,让数据库只能读,然后对整个数据库做逻辑备份(就是将所有数据生成SQL写入备份文件。) + +做逻辑备份有三种方式: + +##### 1.全局锁 + +对数据库执行 + +`Flush tables with read lock`命令让整个库处于只读状态。 + +##### 2.利用innodb的事务隔离性(可重复读) + +就是通过官方自带的逻辑备份工具mysqldump来进行逻辑备份时,可以设置一个参数-single-transaction,这样导数据的时候就会开启一个事务,这样利用innodb的mvcc机制可以保证在事务执行过程中,读到的数据都跟事务开始时的一致,并且执行过程中,其他事务可以执行更新操作, 不会对他造成影响。这种方法必须要求数据库所有表的引擎都是innodb才行。 + +##### 3.set global readonly=true + +执行这个命令也可以让全库只能读,但是第一有些系统会使用readonly来做一个操作,例如根据readonly是否为true判断数据库是否是从库,第二是如果执行这个命令后,客户端断开连接后,数据库会一直处于只读状态,如果是FTWRL命令发送异常会释放全局锁。(如果是从库,设置read-only对super user权限无效) + +### 表级锁 +表级别的锁有两种,一种是表锁,一种是元数据锁MDL。 +##### 表锁 lock table + +就是使用lock table user_table read/write命令来对表进行加读锁或者写锁 +加读锁后,表对所有线程都是只能读,即便是当前线程也只能读表,不然会数据不一致。 +加写锁后,表是对当前线程写,其他线程不能读,不然会数据不一致。 +可以通过unlock tables来解锁,客户端断开时也会自动释放锁,但是影响所有线程,影响面太大了。 + +##### 元数据锁MDL(MetaData Lock) + +分为读锁和写锁,加读锁时,所有的线程都可以读表,加写锁时,只能一个线程写,其他的不能读。 +锁不用显式使用,是访问一个表时,自动加上的。 +对表执行普通SQL语句对表数据进行增删改查时,会加读锁。 +对表结构做修改时,会加写锁。 + +元数据锁是为了修改表结构不会出现问题而设计的,因为一边修改表结构一边读数据可能会读到脏数据,所以在增删改查时会申请读锁,在这个期间不能修改表结构,要修改表结构需要先申请写锁,申请成功后对表结构进行修改,在这个期间不能进行增删改查。 + +##### 自增锁 + +插入语句主要分为两种: +1.能确定插入行数的,例如插入一条或者多条数据,INSERT... +2.不能确定行数的,例如从一个表查询出满足条件的数据,然后插入另外一个表,INSERT...SELECT + +在所有模式中,如果一个事务回滚,这些自增值将被“丢失”。 + +innodb_autoinc_lock_mode为0 +这种是tradition模式,每次执行一条插入语句时都会去申请表级别的auto_increment锁 + +innodb_autoinc_lock_mode为1 +这种是consecutive模式,执行不确定数量的插入语句时,才会去申请表级别的auto_increment锁, +执行确定数量的插入语句时,只需要执行前去获取 AUTO_INCREMENT 计数器的互斥锁并在获取主键后直接释放, +不需要等待当前语句执行完成。 + +innodb_autoinc_lock_mode为2 +交叉模式 所有的插入语句都不需要获取表级别的 AUTO_INCREMENT 锁, +如果binlog_format为statement模式,如果从服务器上的计数器的值可能会与主服务器不一致, +可能会有同一行数据在主从数据库上id不一样的情况,如果binlog_format为row模式,那么就不影响。 + + +##### 意向锁 + +意向锁定的主要目的是表明有人正在锁定表中的行,或者打算锁定表中的行。意向锁的作用主要在于,当一个事务去申请表级别的排斥锁X,共享锁S时,需要去判断是否有其他事务在修改数据行,或者让数据行处于只读状态。假如没有意向锁,可能需要查询每一行数据,判断是否加了行锁。而如果有意向锁的情况下,可以快速进行判断,只需要判断当前表是否有加意向锁就可以了,减小了性能开销。 + +**意向共享锁(IS锁)** + +事务让一行数据只能读,需要申请对这行数据加行级别的锁共享锁S,在申请S锁之前会主动申请表级别的共享意向锁IS锁。 + +**意向排斥锁(IX锁)** + +事务在更新某一行数据时,需要申请对这行数据加行级别的锁排斥锁X,在申请X锁之前会申请IX + +意向锁之间是兼容的,IS锁和IX是兼容,因为可能我们对第一行数据加S锁,那么会申请IS锁,对第二行数据加X锁,此时跟第一行的数据的S锁不冲突,所以也会先申请IX锁,由此可见,IS锁和IX之间不冲突,IS锁,IX锁与行级别的S,行级别的X之间也不冲突。 + +![图片](../static/640-1751368.png) + +意向锁只是跟表级别的S,X锁可能会冲突, + +场景1:假设一个事务要加表级别的S锁,让整个表只能被读。那么如果当前有意向锁IX,说明有其他事务在改数据,那么不能加,只能进行等待,等事务改完是否意向锁IX。 + +场景2:假设当前事务要加表级别的S锁时,让整个表只能被读。只有IS意向锁,没有IX锁,说明只是有其他事务在让数据只能被读取,不能被修改,那么加表级别S锁,也不会其他事务造成影响。 + +场景3:假设当前事务要加表级别的X锁时,让整个表只能被这个事务写,不能被其他事务读。如果现在有其他事务加了意向读锁IS,说明有其他事务在让一些数据行只能被读,或者是一些写锁IX,说明其他事务让一些数据行正在被修改。那么当前要加表级别的X锁就不行,会跟其他事务冲突,只能等其他事务执行完毕才能申请成功。 + +| | 表级别的S锁 | 表级别的X锁 | +| ---------- | ----------- | ----------- | +| 意向读锁IS | 兼容 | 不兼容 | +| 意向写锁IX | 不兼容 | 不兼容 | + +##### 那么意向锁的作用是什么呢? + +假如没有意向锁,我们执行lock table read命令来申请表锁,让整个表只能读,在获得表级别的只读锁之前,需要执行的步骤是: + +1.数据库会先判断当前表是否加了表级别的排斥锁,因为这个时候要是加了排斥锁,是只能由加了那个排斥锁的事务来更新数据,其他事务都不能读数据,只能阻塞等待。 + +2.如果当前表没有加表级别的排斥锁,那么就需要对每一行数据进行判断,判断是否加了行级别的X锁,如果加了只能阻塞等待,这样需要对一行进行判断,性能开销太大了。 + +所以才有了意向锁,在获得表级别的只读锁之前,需要执行的步骤是: + +1.第一步还是跟上面的步骤一一样 + +2.第二步只需要判断当前锁是否加了表级别的意向排斥锁,因为如果加了意向排斥锁,说明正在有事务在对数据加行锁,对数据进行更新,这样避免了对每一行数据进行判断,判断是否加了行锁。 + +#### Innodb的锁 + +##### 行锁 + +* 共享锁 S锁,就是读锁,允许事务读一行数据,不能被修改。所以读锁之间不排斥 + +* 互斥锁 X锁,就是写锁,就是让当前事务可以修改这行数据,其他事务不能修改这行数据 + +##### 记录锁 record lock + + 记录锁定是对单条索引记录的锁定。例如, `SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;` 可以防止从插入,更新或删除行。 + +##### 间隙锁 gap lock + +间隙锁就会对记录之间的间隙加锁,防止数据插入。就是我们在使用实时读(SELECT FOR … UPDATE)或者更新,为了防止读的过程中有新的数据插入,会对我们读的数据的左右区间进行加锁,防止其他事务插入数据,所以间隙锁之间是不排斥的,间隙锁排斥的只是插入数据的操作。 + +##### 下一键锁 next-key lock + + next-key lock就是会锁记录以及记录之间的间隙,就是 record lock 和 gap lock的组合,就是会对索引记录加记录锁 + 索引记录前面间隙上的锁”,就是对要更新的数据的左右两个端点加间隙锁, + +例如num是一个普通索引,非唯一性索引,已有数据是1,5,10,20,30 + +那么 next-key lock可以锁定的区间是 +```java + +(负无穷,1] + +(1,5] + +(5,10] + +(10,20] + +(20,30] + +(30,正无穷) +``` + +```java +//更新操作 +update table set note = '1' where num = 10; +//或者是使用实时读 +SELECT * FROM table WHERE num = 10 for UPDATE; +``` + +如果num是唯一性索引,那么只需要对num为10的这条索引加锁就行了(就加一个Record lock锁),因为不用担心其他事务再插入一条num为10的数据,因为会有唯一性判断。但是如果num是非唯一性索引,为了防止事务执行过程中有num为10的数据插入,那么会对(5,10]和(10,20]这两个区间加锁。 + +#### B树是什么? + +![img](../static/a8773912b31bb05136157756082ed7b24aede072.jpeg) + +平衡二叉树就是每个节点左右子树的高度差小于等于1,B树其实是一个平衡多路查找树,假设是M阶的, + +1.根节点至少有一个关键字。(这里的关键字可以理解为每个节点的子节点) + +2.非根非页节点的关键字数是 需要<=m-1并且>ceil(m/2)-1。 + +3.节点内的元素从左到右递增,左边节点的所有元素值<右边节点的所有元素值。 + +4.叶子节点在同一层,高度一致。 + +跟二叉树相比,因为每个节点关键字会多很多,所以相同的关键字数时,层级会少很多,会减少查找时间和复杂度。 + +https://segmentfault.com/a/1190000020416577 + +https://blog.csdn.net/chai471793/article/details/99563704 + +### B树与B+树的区别是什么? + +B+树是为磁盘存储专门设计的一M阶多路平衡查找树(阶数可以理解为每个节点最多的孩子节点的个数,二叉树就是2阶),所有记录节点都是按照从小到大顺序存放在最后一层的叶子节点上,由各叶子节点的指针相连接。可以认为一个叶子节点就是一个内存页(默认情况下,一个内存页大小为16K),每个内存页里面存储多个数据行,内存页直接通过指针连接,形成一个**双向链表**,所以叶子节点在**逻辑上是连续的**,在**物理上不是连续存储的**,就是每个叶子节点可以存储在不同地址上,通过指针相互连接。每个非叶子节点(也就是索引节点)也是一个内存页,里面存储了很多索引节点的值,但是B+树索引节点只存索引值,不存数据行的数据,这样可以让每个索引内存页存储更多的索引值,这样可以使得B+树的层数更少(这也是B+树比B树更优的地方)。B+树在数据库的中实现一般是只有2到4层,机械磁盘一般1秒可以进行100次IO,也意味着每次在B+树中的查询操作可以在20ms到40ms之间完成。 + +![image-20210128192759236](../static/image-20210128192759236.png) + + + +1.B树每个节点会保存关键字,索引和数据。而B+树只有叶子节点保存数据,其他节点只保存关键字和索引。所以相同的内存空间可以容纳更多的索引节点。 + +2.B+树的所有数据都存在叶子节点上,所以查询会更加稳定,而且相邻的叶子节点都是连接在一起的,更加适合区间查找和搜索。 + +##### B+树与二叉树区别是什么?为什么不用红黑树? + +红黑树是一个平衡的二叉查找树。有以下几个性质: + +1.根节点和叶子节点都是黑色的(这里的叶子节点指的是普通的节点增加的一个黑色的空节点)。 + +2.红色节点的子节点必须是黑色的,也就是不能有两个红色节点连续。 + +3.黑色的节点可以连续,但是从根节点到叶子节点的所有路径包含的黑色节点的个数是一致的。(所以**根节点到叶子节点的最长路径<=最短路径的两倍**) + +红黑树是二叉查找树(也就是每个节点的左子树<当前节点的值,右子树所有节点>=当前节点值),但不是严格意义上的平衡二叉树,因为平衡二叉树要求任何节点的左右子树高度差是<=1,红黑树根节点到叶子节点的最长路径会<=最短路径的两倍,所有他是大致意义上的平衡树。 + +相比于AV树(也就是自平衡的二叉查找树,左右子树高度差不超过1),红黑树插入,删除效率更高。因为不需要保证绝对的平衡,任何不平衡需要的旋转次数不超过3次,即便在最坏的情况下,红黑树能够以O(log(N))的时间复杂度进行搜索、插入、删除操作。 + +##### 与红黑树的比较 + +红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B+ Tree 作为索引结构,主要有以下两个原因: + +(一)更少的查找次数 + +平衡树查找操作的时间复杂度和树高 h 相关,O(h)=O(logdN),其中 d 为每个节点的出度。 + +红黑树的出度为 2,而 B+ Tree 的出度一般都非常大,所以红黑树的树高 h 很明显比 B+ Tree 大非常多,查找的次数也就更多。 + +(二)利用磁盘预读特性 + +为了减少磁盘 I/O 操作,磁盘往往不是严格按需读取,而是每次都会预读。而B+数中存储的叶子节点在内存中是相邻的,这样可以读取会快一些。 + +(三)存储更多的索引节点 + +B+树跟B树的区别就是B+是叶子节点存储数据,非叶子节点(也就是索引节点)只存储索引项,B树是所有节点都存储数据,而每个节点都是磁盘的一个内存页,内存页大小是固定,B+树的每个索引节点可以容纳的索引值更多,与B树相比,B+树的层数更少。 + +### 索引是什么? + +索引可以让服务器快速定位到表的指定位置。 + +优点是 + +1.大大减少了服务器需要扫描的数据量。 + +2.帮助服务器避免排序带来的性能开销。 + +3.将随机IO变成顺序IO。 + +### 字符串索引和数字类型索引的区别? + +因为字符串索引在索引树上两个节点比较会比较慢,数字类型的索引会快一些, + +如果非要用字符串索引可以采用以下解决方法。 + +1.对字符串的前n个字符建立前缀索引,前缀索引不能使用order by。 + +2.增加一列,对字符串转换为整型的hash值address_key=hashToInt(address),对address_key建立索引,查询时同时限定hash值也限定地址。可以用如下查询where address_key = hashToInt(‘beijing,china’) and address = ‘beijing,china’; + +效率的话,100万的数据量,字符串索引查询600ms,数字查询20ms。 + +### union和unionall的区别是什么? + +union就是将两个SELECT语句查询的结果集合并(两个SELECT可以是同一个表,也可以是不同表),如果需要排序,在第二个SELECT语句后加ORDER BY语句,会对所有结果进行排序。 + +union默认是会去除重复的数据的,会对结果集做去重处理,union all不会做去重处理。 + +所以union效率慢一些,如果能确定结果不会重复或者需要不去重的结果,那么应该使用union all,效率会高一些。 + +![](../static/16bd188f482703e1.png) + +### 不设置MySQL主键会怎么样? + +如果没有设置主键,innodb会选择第一个非空唯一索引作为聚集索引。 + +如果没有设置主键,也没有合适的唯一索引,那么会生成一个隐藏的id作为索引的主键,这个值会随着插入而自增。 + +主键如果是自增的,那么插入数据的位置是已知的,而且不用移动已有数据。如果是非自增的,首先需要查找到要插入的位置,近似于随机查找,然后将后面的数据向后移动。 + +### 聚集索引是什么? + +聚集索引与非聚集索引的最主要的区别是:叶节点是否存放一整行记录 + +##### innodb的聚集索引 + +innodb的索引是聚集索引,就是所有数据都存在聚集索引的的叶子节点中,其他二级索引的的叶子节点值存储KEY字段加对应列的主键值,如果使用二级索引查找数据,先根据索引查到二级索引对应的行的主键id,然后根据主键id去聚集索引中查找对应的行的数据。(所以innodb必须要有主键)。 + +聚集索引示意图一: + +聚集索引是依据B+树来实现的,索引节点(也就是非叶子节点)只存储索引值,叶子节点才会存具体的数据行,每个索引节点都是一个内存页(由于只存索引值,相比B树,B+树的每个索引节点的内存页可以存储更多的索引值),每个叶子节点也都是一个内存页,内存页里面数据行按主键id从小到大的顺序存储,然后叶子节点之间是通过指针连接的,形成一个双向链表。所以聚集索引的叶子节点在逻辑上是存储在一起的,在物理上是并不是连续的,而是通过双向链表连接的。假设说叶子节点在物理上的存储也是连续,那么如果中间的叶子节点中大量插入数据行,导致叶子节点的内存页容量不够时,需要后面的叶子节点全部后移,这样维护成本会很高。如果叶子节点只是通过双向链表进行连接,当中间的叶子节点容量不够时,可以新增一个内存页,插入在其中,作为一个叶子节点,只需要修改双向链表中的指针即可。 + +![image-20210128200446778](../static/image-20210128200446778.png) + +聚集索引示意图二: + +![img](../static/3190591-11cabdfdce46f976.JPG) + +![img](../static/6734122-55c8aed7986edbcb.png) + +1.对于**聚集索引表**来说(左图),表数据是和主键一起存储的,主键索引的叶结点存储行数据(包含了主键值),其他列,事务ID,回滚指针,二级索引的叶结点存储行的主键值。使用的是B+树作为索引的存储结构,非叶子节点都是索引关键字,但非叶子节点中的关键字中不存储对应记录的具体内容或内容地址。叶子节点上的数据是主键与具体记录(数据内容)。 + +2.对于**MyISAM的非聚集索引表**来说(右图),表数据和索引是分成两部分存储的,主键索引和二级索引存储上没有任何区别。使用的是B+树作为索引的存储结构,所有的节点都是索引,叶子节点存储的是索引+指向索引对应的记录的数据的指针。 + +##### 聚集索引的优点 + +1.当你需要取出一定范围内的数据时,用聚集索引也比用非聚集索引好。 + +2.当通过聚集索引查找目标数据时理论上比非聚集索引要快,因为非聚集索引定位到对应主键时还要多一次目标记录寻址,即多一次I/O。 + +3.使用覆盖索引扫描的查询可以直接使用页节点中的主键值。 + +##### 聚集索引的缺点 + +1.**随机插入容易造成页分裂**,按照主键的顺序插入是最快的方式,否则将会出现页分裂(就是存储数据行的内存页容量不够,需要新申请一个内存页分担一部分数据行),影响性能。因此,对于InnoDB表,我们一般都会定义一个自增的ID列为主键。 + +2.**更新主键的代价很高,因为将会导致被更新的行移动**。因此,对于InnoDB表,我们一般定义主键为不可更新。 + +3.**二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。** + +二级索引的叶节点存储的是主键值,而不是行指针(非聚集索引存储的是指针或者说是地址),这是为了减少当出现行移动或数据页分裂时二级索引的维护工作,但会让二级索引占用更多的空间。 + +4.**采用聚集索引插入新值比采用非聚集索引插入新值的速度要慢很多**,因为插入要保证主键不能重复,判断主键不能重复,采用的方式在不同的索引下面会有很大的性能差距,聚集索引遍历所有的叶子节点,非聚集索引也判断所有的叶子节点,但是聚集索引的叶子节点除了带有主键还有记录值,记录的大小往往比主键要大的多。这样就会导致聚集索引在判定新记录携带的主键是否重复时进行昂贵的I/O代价。 + +### 联合索引是什么? + +联合索引就是多列索引,就是可以多个字段建立一个索引,并且是最左前缀匹配元素, + +``` +create index a_b_c on user(a,b,c) +这样相当于是创建三个索引 +a +a,b +a,b,c +``` + +就是在非聚集索引对应的B+树中,索引的排序是先比较a的大小,再比较b的大小然后再比较c的大小。并且是遇到范围比较时就会停止匹配。 + +1. 最左前缀匹配原则,MySQL会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如 a="3" and="" b="4" c="">5 and d=6,如果建立(a,b,c,d)顺序的索引,d是无法使用索引的,如果建立(a,b,d,c)的索引则都可以使用到,a、b、d的顺序可以任意调整。 +2. =和in可以乱序,比如 a=1 and b=2 and c=3 建立(a,b,c)索引可以任意顺序,MySQL的查询优化器会帮你优化成索引可以识别的形式。 + +例如: + +``` +select * from user where a > 1; +SELECT * FROM user WHERE a='2222' OR b='13281899972'//这个就不会走联合索引,因为只是查询b时不能根究索引查询,所以还是需要全表扫描,所以干脆a也不用索引了。 +如果a,b都有索引,那么会单独根据a,b来查询,然后将结果集合并 +关于or查询的真相是: +所谓的索引失效指的是:假如or连接的俩个查询条件字段中有一个没有索引的话,引擎会放弃索引而产生全表扫描。 +``` + +这样的语句可以走联合索引,但是不会走联合索引,因为符号这个范围的数据很多,查出之后,只能获得这些数据的主键,还需要根据主键去聚集索引中查,效率比较低,还不如直接全部扫描,所以直接去聚集索引下顺序得对全表进行扫描。 + +```sql +SELECT * FROM user WHERE age=1 and height = 2 + +SELECT * FROM user WHERE age=1 and weight=7 + +SELECT * FROM user WHERE weight=7 and age=1 +``` + +这些是可以走联合索引的, + +```sql +SELECT * FROM user WEHRE age=1 and height>2 and weight=7 +``` + +这个也会走联合索引,查出age=1的索引,然后查出height>2的所有数据,因为height是一个范围查找,所以到weight就不会用索引,会将将这些数据载入内存,根据weight进行筛选。 + +##### 索引没有被采用? + +1、根据搜索条件,找出所有可能使用的索引 +2、计算全表扫描的代价 +3、计算使用不同索引执行查询的代价 +4、对比各种执行方案的代价,找出成本最低的那一个 。 + +### 覆盖索引是什么? +就是假如有一个联合索引(a,b,c),如果我们只是需要a,b,c这几个字段的数据,查询时就不需要根据主键id去聚集索引里面回表查询了。 + +```SQL +SELECT a,b,c FROM user where a = 1 +``` + +哪些情况需要建索引: +1. 主键,唯一索引 +2. 经常用作查询条件的字段需要创建索引 +3. 经常需要排序、分组和统计的字段需要建立索引 +4. 查询中与其他表关联的字段,外键关系建立索引 + +### 哪些情况不要建索引? + +1. 表的记录太少,百万级以下的数据不需要创建索引 + +2. 经常增删改的表不需要创建索引 + +3. 数据重复且分布平均的字段不需要创建索引,如 true,false 之类。 + +4. 频发更新的字段不适合创建索引 + +5. where条件里用不到的字段不需要创建索引 + +### 主键,唯一性索引,普通索引的区别是什么? + +一个表中可以有多个唯一性索引,但只能有一个主键。 + +1.在查询时,如果是使用的是主键,或者是唯一性索引查询,查到后就返回了,普通索引还会继续向后遍历,直到第一个不满足条件的才会返回,普通索引会多检索几次。几乎没有什么影响。 + +2.更新时普通索引需要判断唯一性。 + +3.主键是聚集索引,普通所以是非聚集索引。 + +### InnoDB和MyISAM的区别是什么? +##### InnoDB + +是 MySQL 默认的事务型存储引擎,只有在需要它不支持的特性时,才考虑使用其它存储引擎。 + +实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ Next-Key Locking 防止幻影读。 + +主索引是聚集索引,在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升。其次是聚集索引每个叶子节点按照主键id从小到大顺序存储了大量数据行,而每个叶子节点在实现上是一个内存页,磁盘IO读取时是读取一个内存页,会进行一定的预读,所以在进行范围查找时,InnoDB引擎效率更高,而MyISAM则需要对查找范围内所有数据行进行随机读取。(每条数据都是存储在不同地址上) + +内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够加快读操作并且自动创建的自适应哈希索引、能够加速插入操作的插入缓冲区等。 + +支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。 + +##### MyISAM + +不支持事务,不支持行级锁,只能全表加锁,读取时会对所有表加共享锁,写入时会对表加排他锁。 + +### 什么是分库分表? + +一般认为单表数据量在1000万时,查询效率是最合适的,如果超过1000万,执行性能就会下降,可以考虑分库分表。 + +##### 垂直切分 + +就是根据列进行分表,例如根据业务,来对表进行拆分成不同的表,或者根据使用频率,将常用的列分在一个表里面,将不常用的列分在一个表里面。 + +##### 水平切分 + +将表根据行来进行分表,将一个表拆分成多个表结构相同的表。 + +第一种 一般是用主键ID对数据表的数量进行取模运算,得到的余数就是新表的位置。(如果是字段是字符串,那么就根据字符串计算出哈希值,然后除以表的数量,得到新库的位置) + +第二种 根据时间来拆分,主表只存储最近两个月的数据,副表存储之前的数据。这种主要是适合哪种访问的数据跟时间相关性比较大的情况,例如统计,我有看某新闻网站他们的技介绍,他们的文章会有一个PV,UV统计表,就是每天大概有200万的文章有PV,UV,也就是数据库每天会新增200万行的数据,一般来说查文章近期的UV,PV会多一些,查昨日PV,一周PV,或者一个月的PV会多一些。所以是安装时间来划分热库和冷库,一月一个表,或者一天一表。 + +##### 问题 + +1.事务问题,如果在事务里面操作一个表,然后再操作另外一个表,效率会比较低,然后也比较麻烦。 + +2.跨库join的问题 + +在拆分之前,系统中很多列表和详情页所需的数据是可以通过sql join来完成的。而拆分后,数据库可能是分布式在不同实例和不同的主机上,join将变得非常麻烦。而且基于架构规范,性能,安全性等方面考虑,一般是禁止跨库join的。那该怎么办呢?首先要考虑下垂直分库的设计问题,如果可以调整,那就优先调整。(就是尽量不要去分库)如果无法调整的情况,下面有几种解决方案: + +1.全局表 + +所谓全局表,就是有可能系统中所有模块都可能会依赖到的一些表。比较类似我们理解的“数据字典”。为了避免跨库join查询,我们可以将这类表在其他每个数据库中均保存一份。同时,这类数据通常也很少发生修改(甚至几乎不会),所以也不用太担心“一致性”问题。 + +##### 分库分表具体实施 + +1.停机迁移法 + +2.迁移历史数据,新的更新发kafka消息 + +就是对updatet_time小于某个时间点的数据全部拷贝出来,迁移到新的数据库,同时项目在执行增删改相关的SQL时,同时往Kafka中发一份,迁移结束后,用订阅程序消费kafka消息,更新新库,消费完之后,然后校验一致性切数据库。 + +缺点就算侵入性太强了,所有更新数据库的业务项目都需要改动,然后给kafka发消息。 + +3.迁移历史数据,订阅bin log接受更新 + +还是先迁移数据,迁移完之后用订阅程序消费bin log消息,更新新库,消费完之后,然后校验一致性,切数据库。 + +### 怎么验数据一致性 + +这里大概介绍一下吧,这篇的篇幅太长了,大家心里有底就行。 +(1)先验数量是否一致,因为验数量比较快。 +至于验具体的字段,有两种方法: +(2.1)有一种方法是,只验关键性的几个字段是否一致。 + +随机取小批量数据,编码后比较。 + +(2.2)还有一种是 ,一次取50条(不一定50条,具体自己定,我只是举例),然后像拼字符串一样,拼在一起。用md5进行加密,得到一串数值。新库一样如法炮制,也得到一串数值,比较两串数值是否一致。如果一致,继续比较下50条数据。如果发现不一致,用二分法确定不一致的数据在0-25条,还是26条-50条。以此类推,找出不一致的数据,进行记录即可。 + + +### 怎么实现跨库分页查询? + +##### 全局视野法 + +就是例如要查询第三页的数据,那么把每个库里面前三页的数据都查询出来,然后排序, + +优点是比较准确,也比较简单 + +问题就是每个库返回的页数很多,网络传输量很大,而且收到数据后的需要二次排序,排序的性能消耗也比较大。 + +``` +要查order by time offset offset_value limit num时 +对每个库查 order by time offset 0 limit offset_value + num然后进行排序 +``` + +##### 最大时间法 + +就是禁止跳页查询,就是如果业务不需要跳页查询的话,一开始查第一页的时候,从每个库取一页数据回来进行排序后返回。 + +查第二页数据时根据第一页结果中最大的时间去每个库再取一页数据,然后进行排序得到第二页数据。 + +优点是是每次直插一页,传输数据量小,排序的数据量也小, + +缺点是由于查第二页及以后的页数都需要知道上一页最大时间。 + +```java +要查order by time offset offset_value limit num时 +获得上一页的最大时间 +对每个库查 where time >lastTime order by time offset 0 limit num然后进行排序 +``` + +##### 每库平均取法 + +假设数据分布足够平均,每个库的数据分布都是平均的,假设有四个库,查第一页数据时,每个库都取前0.25页数据,然后合并后返回,取第二页数据时,每个库再都取0.25页。但是这样取得是不准的数据。 + +``` +将order by time offset X limit Y,改写成order by time offset X/N limit Y/N +``` + +##### 二次查询法 + +假如要取offset 为X,limitY的数据, + +1.假设有四个库,需要去每个库查offset为X/4,limit为Y的数据,然后得到四个结果集,每个结果集的time都有一个时间最小的time_min,时间最大的time_max结果,取四个结果集中最小的time_min + +2.然后去每个库去查between time_min到之前每个库的time_max的结果集, + +3.因为之前每个库的time_max的offset都是X/4+Y,所以可以反推出time_min在每个库里的offset,然后得到全局offset, + +4.然后根据全局offset可以计算我们想到的offset X后Y个数据,就是以time_min为起点到距离我们offset的差值。 + +关键在于两次查询中间,如果数据库删除过这个区间内的数据,time_min到time_max之间删除过数据,这样就不准, + +```sql +(1)将order by time offset X limit Y,改写成order by time offset X/N limit Y + +(2)找到最小值time_min + +(3)between二次查询,order by time between time_min and time_i_max + +(4)设置虚拟time_min,找到time_min在各个分库的offset,从而得到time_min在全局的offset + +(5)得到了time_min在全局的offset,自然得到了全局的offset X limit Y +``` + +首先需要查询两次,而且如果表数据是按照分段来查询的,会有问题。 + +参考链接: + +https://blog.csdn.net/uiuan00/article/details/102716457 + +##### Zset记录法 + +例如像百度贴吧,假设一个吧里面允许跳页查询的页数为前100页,每页为20个,,每次顶贴都会把贴子顶到最前面,那么可以使用一个Zset来存储最近的2000个帖子的基本信息(id,标题,图片,等足够贴吧列表页展示的信息),每个帖子的score就是最近更新时间,帖子在Zset中按照更新时间排序。然后每次顶贴都来维护这个Zset就可以了。跳页也可以直接以Log(N)复杂度去Zset中查询到这个帖子。 + +### 怎么为订单系统设计数据库的表? + +假设说一个订单系统,用户发起一个订单后,在用户端的需要查看个人订单列表。在商家端也需要查看这个商家最近订单列表。如果用户量比较大肯定是会需要分库分表,比较好的方案是在用户订单表user_order里面存储一个订单表(主键id可以是每个分库自己自增的,也可以是使用snowflake算法生成的),但是用户订单表user_order会根据用户uid进行取模运算,进行分库分表。也就是同一个用户的所有订单都在一个数据库的表中,避免查询时需要多库多表查询。 + +但是此时商家去查询订单列表时,各个订单还是分布在每个分库的user_order表中,为了避免多库查询,还会维护一个商家表business_order,(主键id可以是每个分库自己自增的,也可以是使用snowflake算法生成的),但是商家订单表business_order会根据商家id进行取模运算,得到一个数字,每个商家的订单都存在同一个表中,以此实现分库分表。 + +一般来说user_order可以存所有的订单信息,business_order表只存储少量商家需要的订单信息。 + +### MySQL主从复制的工作流程是什么样的? + +就是将主节点的数据复制到从节点。 + +用途: + +1.可以读写分离,主库用来写数据,从库用来读数据。在更新数据时,会对整个表加锁,如果是读写分离的,可以去从表读取数据,这样就不会有问题。 + +2.做备份,主库出故障后,进行故障转移,让从库代替主库,提供服务。 + +##### 实现原理 + +**主节点日志发送线程** + +当主节点和从节点建立连接后,主服务器上会起一个bin log dump线程,用于给从节点发送bin log日志(日志所包含的信息之外,还包括本次返回的信息的bin-log file 的以及bin-log position),在读取bin log日志时,会对日志文件加锁,读取完成后会解锁。 + +**从节点I/O线程** + +从节点与主节点建立连接后会起一个I/O线程来接受主节点发送过来的bin log日志内容,并且保存在从节点的relay log文件中,保存成功后就会给主节点回复ACK消息,表明接收成功。 + +**从节点SQL线程** + +从节点同时会起一个SQL线程,来读取 relay log 中的内容,解析成SQL,并且在从节点上执行,保证和主节点的数据一致性。 + + + +![img](https://pic1.zhimg.com/80/v2-1b0c3f31bd398c39b9e0930059b0ca24_1440w.jpg) + +![MySQL主从复制原理及配置实现](../static/1460000008942623.jpeg) + +复制模式 + +##### 异步模式(默认的模式) + +主节点不会主动push bin log给从节点,也不会管从节点的同步情况,默认就是这种模式。 + +##### 半同步模式(MySQL 5.5之后提供) + +主节点给从节点发送bin log 之后,会一直等待回应,只要一个从节点接受bin log,并且写入relay log 成功,给主节点返回接受成功的ACK信息,主节点就认为成功,提交事务。 + +##### 全同步模式 + +就是需要所有的从节点接受日志,并且写入relay log 成功,给主节点返回接受成功的ACK信息,主节点才认为成功,提交事务。 + +### bin log格式 + +**Statement-base Replication (SBR)语句模式** + +就是执行什么更新的SQL,就将这些SQL保存到 bin log日志文件中。 + +缺点在于可能某些情况下导致数据不一致,例如根据now()当前服务器的时间,可能从节点执行语句时的时间跟主节点的语句时间不一样。 + +**Row-based Relication(RBR) 数据行模式** + +就是更新那些数据行,将这些更新的数据行生成SQL,保存在bin log日志文件中。 + +优点是能确保数据的精准,缺点是会产生大量的日志,日志内容会变大,尤其是一行SQL对大量数据行更新时,而且也不能通过bin log解析当时执行的SQL语句。 + +**Mixed-format Replication(MBR)混合模式** + +MySQL NDB cluster 7.3 和7.4 使用的MBR。是以上两种模式的混合,对于一般的复制使用STATEMENT模式保存到binlog,对于STATEMENT模式无法复制的操作则使用ROW模式来保存,MySQL会根据执行的SQL语句选择日志保存方式 + +**支持版本** + +MySQL 5.1.5 之前 binlog 的格式只有 STATEMENT,5.1.5 开始支持 ROW 格式的 binlog,从 5.1.8 版本开始,MySQL 开始支持 MIXED 格式的 binlog。 + +MySQL 5.7.7 之前,binlog 的默认格式都是 STATEMENT。 + +在 5.7.7 及更高版本中,binlog 的默认格式才是 ROW。 + +![](../static/v2-d9ac9c5493d1d772f5bf57ede089f0d5_1440w.jpg) + +#### 死锁 + +要发生死锁,一般需要满足以下四个必要条件: + +1**.资源互斥**:就是资源不能共享,提现在InnoDB中就是一个事务申请了锁,其他事务就不能申请。 + +2.**不可抢占**:就是一个事务没有获得锁,只能等待拥有锁的事务释放锁,而不能去直接抢占锁。 + +3.**占有且等待**:就是没有获得锁的事务只能进行等待,并且也不会释放自己已拥有的锁。 + +4.**循环等待**:就是事务之间拥有的资源必须形成了一个环,例如事务A拥有一些事务B需要的资源,同时事务A也在申请事务B当前拥有的一些资源。 + +**一、预防死锁的方法:** + +预防死锁的思路主要还是从破坏死锁发生需要的必要条件入手,**资源互斥**这个条件没法去破坏, + +**1.破坏不可抢占条件**:就是当一个进程发现自己申请不到某个资源时,那么就把当前持有的资源全部释放掉,待以后需要使用的时候再重新申请。这意味着进程已占有的资源会被短暂地释放或者说是被抢占了。 + +该种方法实现起来比较复杂,且代价也比较大。释放已经保持的资源很有可能会导致进程之前的工作白费了,反复的申请和释放资源会导致进程的执行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。 + +**2.破坏占有且等待条件**: + +**方法1:执行前申请所有资源** + +所有的进程在开始运行之前,必须一次性地申请其在整个运行过程中所需要的全部资源,申请成功,进程才能启动。 + +​ 优点:简单易实施且安全。 + +​ 缺点:因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,可能会造成每次能启动并运行的进程过少,造成资源浪费。 + +**方法2:边执行边释放资源** + +该方法是对第一种方法的改进,允许进程只获得运行初期需要的资源,便开始运行,在运行过程中逐步释放掉分配到的已经使用完毕的资源,然后再去请求新的资源。这样的话,资源的利用率会得到提高,也会减少进程的饥饿问题。 + +**3.破坏“循环等待”条件** + +可将每个资源编号,当一个进程占有编号为i的资源时,那么它下一次申请资源只能申请编号>i的资源。这样资源之间就不会形成环。但是也会造成资源利用率的降低,例如有些进程一开始申请了资源2,哪怕不存在进程竞争,它后续如果需要资源1也申请不到。如下图所示: + +![img](../static/2018051322430635.png) + +**二、避免死锁 ----- 在使用前进行判断,只允许不会产生死锁的进程申请资源** + +这种方法就是在进程申请资源时,系统首先判断**当前可用资源是否够申请**,不够就肯定没法让这个进程运行,就让进程继续等待。够的话继续进行判断,假设把这些资源分配给这个进程,然后通过银行家算法判断**系统是否处于安全状态**,不安全那就不分配,让进程继续等待。 + +银行家算法判断流程: + +假设进程队列里面有P1,P2,P3三个队列,它们持有了一些资源,并且后续执行时还需要资源才能执行完成,假设此时来另一个进程P0,系统判断是否需要给P0分配资源银行家算法先**试探**的分配给它(当然先要看看当前资源池中的资源数量够不够),若申请的资源数量小于等于Available,然后接着判断分配给P0后剩余的资源,能不能使进程队列的某个进程执行完毕,若没有进程可执行完毕,则系统处于不安全状态(即此时没有一个进程能够完成并释放资源,随时间推移,系统终将处于死锁状态)。 + +若有进程可执行完毕,则假设这个进程执行完毕,然后释放已分配给它的资源(剩余资源数量增加),把这个进程标记为可完成,并继续判断队列中的其它进程能否在现有资源数量下执行完毕,**若所有进程都可执行完毕,则系统处于安全状态,并根据可完成进程的分配顺序生成安全序列**(如{P0,P3,P2,P1}表示将P0需要的资源先分配给P0–>P0执行完毕后,P0的资源被回收–>P3所需资源<现有资源,P3执行完毕–>P3的所有资源被回收–>分配给P2–>······满足所有进程执行完毕)。 + +**死锁的检测及解除** + +死锁预防和避免都是对资源分配进行适当限制,属于事前措施,并不利于系统资源的充分共享。而死锁检测不会试图阻止死锁,即在死锁发生前不会做任何操作,只是通过设置的检测机制,检测当前是否发生死锁。若发生死锁,则采取一些措施来解除死锁。 + +判断死锁的法则主要基于第四条死锁的必要条件: + +1.资源分配路径中没有环路,则系统不会出现死锁 + +2.资源分配路径中存在环路,则系统可能出现死锁(如果环路中每种资源只有一个,那么就肯定形成死锁了,如果每种资源有多个,就可能发生死锁) + +解决死锁的方法: + +1.资源剥夺法:剥夺陷入死锁的进程所占用的资源,但并不撤销此进程,再将这些资源分配给需要的进程,直至死锁解除。 + +2.进程撤销法 + +一次性撤销陷入死锁的所有进程,回收所有占用的资源,等死锁解除后,再重新运行进程。 + +逐个撤销陷入死锁的进程,依次回收其资源并重新分配,直至死锁解除。可以优先撤销优先级低、预计剩余执行时间最长、CPU消耗时间少的进程。 + +##### MySQL中的死锁 + +在MySQL中,死锁就是两个或多个事务在同一资源上相互占用,并且请求对方的占用的资源。InnoDB目前处理死锁的方法就是将持有最少行级排他锁的事务进行回滚。 + +除了单条更新语句外,事务获取行锁都是逐步获取的,所以有可能会造成死锁。 +解决死锁有两种策略: +1.超时放弃等待 +innodb_lock_wait_timeout,默认是50s,超时会报错。 +2.死锁检测 +innodb会有死锁检测,但是会消耗一些cpu资源,检测到死锁会让占有锁最少的事务回滚,然后释放锁。 +3.控制并发度 +就是控制访问相同资源的并发事务量。例如将长事务拆分成短事务,这样每次事务占用时间也少,也可以减少其他事务的等待时间,减少死锁发生的概率。 + + +### 怎么优化数量查询? +在innodb引擎下, +##### COUNT(*)和Count(id) +SELECT Count(\*)其实是跟SELECT Count(id)是等价的,会去主键的聚集索引下扫描每一行,然后判断行是否为Null,不为Null计入Count。 +##### Count(col) +也是全表扫描,判断这一行的col值是否为null,不为null,计入Count +##### 怎么优化count(\*)? +可以使用查询一个非空的唯一索引键的数量来替代count(\*),因为count(\*)需要遍历主键的聚集索引的叶子节点,读取每一行的数据,而Count(unique_key)会去unique_key的索引下读取每个叶子的节点,因为每个叶子节点只包含unique_key和主键id,数据大小比聚集索引下的叶子节点下,IO会小一些。 + +##### Myisam可以缓存count,而innodb不能缓存count +因为innodb有事务的概念,如果是在PR的隔离级别下,每个事务查询的count应该等于事务开始时count+本事务执行过程中对count的改变,但是由于每个事务可以单独设置会话隔离级别,所以很难实现对count的缓存。 + +##### 怎么优化慢查询? + +1.首先根据explain+SELECT语句执行,查看结果, + +``` +EXPLAIN SELECT * FROM res_user ORDER BYmodifiedtime LIMIT 0,1000 +``` +``` + +得到如下结果: + +table | type | possible_keys | key |key_len | ref | rows | Extra EXPLAIN列的解释: + +- table 显示这一行的数据是关于哪张表的 +- type 这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、index和ALL +- ref 表里面的哪个索引被用到了 +- rows 显示需要扫描行数 +- key 使用的索引 + + const就是针对主键或者唯一性索引的等值查询,通过索引查找一次就行了。仅仅是查一条数据。 + + ref_eq 唯一性索引查询,一般使用一个表去join另外一个表的唯一性索引字段时,就是ref_eq + + ref 非唯一性索引查询,就是根据普通索引去查找数据,例如查询条件是where a = 1,a是普通索引,但是不是唯一性索引 + + range: 表示有范围条件的全索引范围查询,跟index全索引扫描相比就是有查询条件,可以减少一些扫描的数据量。同时除了显而易见的between,and以及'>','<'外,in和or也是索引范围扫描。 + + index: 表示全索引扫描(full index scan), 这是另外一种全表扫描,例如order by 字段是索引字段,如果是直接去聚集索引下全表扫描,那么查询出来的结果集还需要在内存中排序一边,如果是去非聚集索引下进行全表扫描,然后按照扫描顺序进行回表,回表的顺序就是order by的顺序,可以减少排序的时间,但是会有回表的开销。 + + all:这个就是全表扫描数据文件,然后再在server层进行过滤返回符合要求的记录。 +``` + +https://blog.csdn.net/dennis211/article/details/78170079 + +主要分为以下几个方法: + +**1.减少请求的数据量** + +列方面,避免使用SELECT *,只返回必要的列。 + +行方面,使用limit语句来限制返回的数据行数。 + +对频繁访问的数据加缓存,存在redis中。 + +**2.减少表扫描的行数** + +主要通过使用索引和命中索引来实现,例如如果需要根据发布时间查询最近一天的文章,那么可以根据postTime建立辅助索引,避免进行全表扫描。 + +对于一些常用的条件查询字段,建立联合索引,使用一些联合索引,可以减少查询次数,也可以减少磁盘空间占用。而且当查询的字段在索引中已经包含时,就会使用到覆盖索引,此时在辅助索引中查到需要的字段后就不用再根据主键id进行回表查询了。 + +**4.避免在查询时,对索引字段进行计算和使用函数。** + +这样会导致不通过索引查询,将一些varchar类型的字段与整型数据进行比较时,会触发隐式类型转换,从而使用函数。 + +**5.切分大查询** + +因为大查询在查询时可能会锁住很多数据,也需要获取到这些数据的行锁才能进行查询,切分成小查询可以减少锁竞争,减少等待获取锁的时间。 + +##### 1.使用show profile对一条SQL查询分析当前会话中语句执行的资源消耗情况 + +1.profiling配置默认是不开启的,可以使用set profiling = ON;命令将配置暂时打开。 + +2.执行一条查询SQL + +3.使用show profiles可以查看最近15条查询SQL及对应的查询idquery id + +4.假设查询id为9,使用这个命令show profile for query 9;可以查看每个步骤及其消耗的时间。 + +``` +mysql> show PROFILE for QUERY 9; ++----------------------+----------+ +| Status | Duration | ++----------------------+----------+ +| starting | 0.000054 | +| checking permissions | 0.000007 | +| Opening tables | 0.000116 | +| init | 0.000019 | +| System lock | 0.000009 | +| optimizing | 0.000004 | +| statistics | 0.000011 | +| preparing | 0.000010 | +| executing | 0.000002 | +| Sending data | 0.000061 | +| end | 0.000005 | +| query end | 0.000006 | +| closing tables | 0.000006 | +| freeing items | 0.000031 | +| cleaning up | 0.000010 | ++----------------------+----------+ +``` + +https://www.cnblogs.com/116970u/p/11004431.html + +https://www.jianshu.com/p/1efdddf3d461 + +### char类型与varchar类型的区别? + +char类型 + +char类型是定长的,在内部存储时实际使用长度较短时会在右边用空格填充,所以插入的数据如果右边有空格会自动截断,因为没有办法知道是自带的还是填充的。 + +对英文字符(ASCII)占用1个字节,对一个汉字占用两个字节。 + +适合每行数据的长度比较平均的情况,否则会造成存储空间的浪费。 + +varchar类型 + +是不定长的,有一个字节用来存储长度(当长度大于255时,使用两个字节来存储长度),内部存储时,使用多少长度,内存中就占用多少长度,不会有空余,所以比较节省空间。 + +varchar的存储方式是,对每个英文字符占用2个字节,汉字也占用2个字节。 + +效率上其实varchar会好一点,其实网上没有比较详尽的测试,看一个博客对1000w的数据进行测试时,varchar会略高一点。 + +### 如何优化MySQL慢查询? +首先对EXPLAIN 分析查询语句后extral字段中出现的一些参数进行说明: +##### Using index +使用了索引进行查询。 +##### Using where + +一般就是where条件中一些判断字段没有索引,那么这个条件的过滤就会在Server端进行 在innodb数据引擎将结果返回给Server层后,MySQL Server层对数据进行进一步的过滤,然后返回结果。 + +例如: + +假设test表只有主键id有索引,name没有索引,那么查询时就是把所有满足id > 100的数据行返回给Server层,然后Server层根据name='123'进行过滤,所有使用explain时会发现extra那里显示的是Using where + +```sql +SELECT * FROM test where id > 100 AND name = '123' +``` + +##### Using index condition + +这个就是在MySQL5.6以后,做的一个优化,例如有一个联合索引(a,b,c) + +查询条件为**where a = '123' AND b like '%aa% AND c like '%cc%'**时,按照联合索引的最左匹配法则,只有a可以用上索引,以前版本的MySQL中,innodb存储引擎就会在联合索引把满足a = '123'的数据的主键id找出来,然后回表,然后把所有数据发送给Sever端,Server端根据b like '%aa% AND c like '%cc%'对数据进行过滤, + +现在有这个push down优化,因为联合索引中有b和c两个字段的信息,innodb在联合索引查找时就会考虑到b like '%aa% AND c like '%cc%'条件,所有回表的数据就都会是满足这三个条件的,然后返回给Server端的也都是满足条件的数据行,Server端就不会再自己进行数据过滤了。 + +https://dev.mysql.com/doc/refman/5.7/en/index-condition-pushdown-optimization.html + +##### Using filesort + +如果在关联表查询时,Order By的所有字段都来自第一个表(也就是驱动表),那么在处理驱动表时,从驱动表中取出满足条件的结果集时就会进行排序,不需要使用临时表存储数据行进行重排序,extral那一列就会显示是Using index。如果Order By的字段不在索引中,那么就需要在查找出结果集后,进行重排序,就会显示Using filesort。 +##### Using temporary; Using fileSort + +再进行关联表查询时,如果Order By中的字段不全是来自驱动表,也就是使用了被驱动表中的字段进行排序,那么会把关联结果全部查找出来,存放在临时表中,等所有的关联都结束后,再在内存中对数据行进行排序。 + +##### 优化的方法: + +1.优化索引使用情况。使用explain SQL查看解析结果,首先看结果中Extra那一列是否有Using Index,如果没有看是否是where判断条件的字段没有添加索引,不能使用索引。如果出现的是Using Where,可能是where子句里面判断的字段没有加索引,这样innodb就会把所有数据行查询出来,返回给MySQL Server层,Server层做的过滤。 + +2.减少扫描的行数。查看explain SQL解析结果中rows那一列,看行数是不是特别多,通过添加索引的方式减少扫描的行数。 + +3.只查询我们需要的列。看SQL中的查询字段是不是都是我们需要的,只选取我们需要的字段,而不是所有查询SQL都是使用SELECT *,这些多的字段的存在,会增大查询的时间,以及网络传输的数据量。 + +4.优化join的方式,一般join的字段在被驱动表中有索引,那么join使用的算法就会是index Nested-Loop Join,如果没有索引那么就是block Nested-Loop Join。尽量让join的字段有索引可以使用,是在不行,可以增加join buffer Size的大小(默认是256K)。 + +5.如果单表数据量大于1000万,考虑进行分库分表。 + +### 索引的创建步骤是怎么样的? + +通过ALTER TABLE ADD/DROP或者CREATE/DROP INDEX 可以创建和删除索引。 + +```java +//ALTER 命令可以为表增加主键索引,唯一性索引,普通索引 +ALTER TABLE table_name add primary key (column_list) ; +ALTER TABLE table_name ADD INDEX index_name (column list); +ALTER TABLE table_name DROP INDEX index_name (column list); +//CREATE 命令可以为表增加唯一性索引,普通索引 +CREATE index index_name on table_name (column_list); +DROP index index_name on table_name (column_list); +``` + +MySQL接受到创建索引的命令后,会进行创建索引 + +##### Fast Index Creation + +在MySQL 5.5之前创建索引主要是通过 + +1.创建临时表,表结构是添加索引后的表结构 + +2.将原表数据导入到临时表 + +3.删除原表 + +4.将临时表重命名成原来的表名 + +Innodb在1.0之后支持Fast Index Creation,就是添加辅助索引(主键以外的索引),不需要重建表,只需要对表加S锁(加锁期间表只能读,不能写),然后创建索引,对主键加索引还是需要重建表。 + +##### Online DDL + +就是innodb在创建索引时,会将数据库的增删改命令写入缓存日志,创建完毕后通过重放日志来保持数据库的最终一致性。 + +### MySQL的join的实现是怎么样的? + +1.什么是Nested-Loop Join? +2.Index Nested-Loop Join怎么优化连接? +3.Block Nested-Loop Join怎么优化连接? + +**Nested-Loop Join** +在Mysql中,使用Nested-Loop Join的算法思想去优化join,Nested-Loop Join翻译成中文则是“嵌套循环连接”。 + +举个例子: +```SQL +select * from t1 inner join t2 on t1.id=t2.tid +``` +(1)t1称为外层表,也可称为驱动表。 +(2)t2称为内层表,也可称为被驱动表。 + +//伪代码表示: +```java +List result = new ArrayList<>(); +for(Row r1 in List t1){ + for(Row r2 in List t2){ + if(r1.id = r2.tid){ + result.add(r1.join(r2)); + } + } +} +``` +在Mysql的实现中,Nested-Loop Join具体有3种实现的算法: + +Simple Nested-Loop Join:SNLJ,简单嵌套循环连接 +Index Nested-Loop Join:INLJ,索引嵌套循环连接 +Block Nested-Loop Join:BNLJ,缓存块嵌套循环连接 +在选择Join算法时,会有优先级,理论上会优先判断能否使用INLJ、BNLJ: +Index Nested-LoopJoin > Block Nested-Loop Join > Simple Nested-Loop Join + +##### 一.Simple Nested-Loop + +简单嵌套循环连接实际上就是简单粗暴的嵌套循环,如果table1有1万条数据,table2有1万条数据,那么数据比较的次数=1万 * 1万 =1亿次,这种查询效率会非常慢。实现伪代码就跟上面的一样。 + +所以Mysql继续优化,然后衍生出Index Nested-LoopJoin、Block Nested-Loop Join两种NLJ算法。在执行join查询时mysql会根据情况选择两种之一进行join查询。 +##### 二.Index Nested-LoopJoin(减少内层表数据的匹配次数) + +索引嵌套循环连接是基于索引进行连接的算法,索引是基于内层表的,通过外层表匹配条件直接与内层表索引进行匹配,避免和内层表的每条记录进行比较, 从而利用索引的查询减少了对内层表的匹配次数,优势极大的提升了 join的性能: +**原来的匹配次数 = 外层表行数 * 内层表行数** +**优化后的匹配次数= 外层表的行数 * 内层表索引的高度** + +伪代码实现: + +```java +select * from t1 inner join t2 on t1.id=t2.tid +/* +假设要执行上面的这个查询,然后t1作为外层表(也就是驱动表),每次从t1表中取一行数据出来,然后根据t1.id去t2表的聚集索引下查询主键id等于t1.id的数据行,如果存在,就添加到结果集中,所以 + 每次查询的匹配次数=t2表的聚集索引的层数 + 总的查询匹配次数=外层表t1的行数 * t2表的聚集索引的层数 + */ +List result = new ArrayList<>(); +for(Row r1 in List t1){ + Row> r2 = searchBTree(t1.id); + result.add(r1.join(r2)); +} +``` + +使用场景:只有内层表join的列有索引时,才能用到Index Nested-Loop Join进行连接。 + +使用**Index Nested-Loop Join**算法时SQL的EXPLAIN结果extral列是**Using index**。 + +由于用到索引,如果索引是辅助索引而且返回的数据还包括内层表的其他数据,则会回内层表查询数据,多了一些IO操作。 + +##### 三.Block Nested-Loop Join(减少内层表数据的循环次数) + +当有时候Join字段没法使用索引的时候,那样就不能用Index Nested-Loop Join的时候,次数默认就会使用Block Nested-Loop Join。 + +大体的原理 + +Block Nested-Loop Join通过一次性缓存多条数据,把参与查询的列缓存到Join Buffer 里,然后拿join buffer里的数据批量与内层表的数据进行匹配,从而减少了内层循环的次数(遍历一次内层表就可以批量匹配一次Join Buffer里面的外层表数据)。 + +伪代码表示 + +```java +select * from t1 inner join t2 on t1.tid=t2.tid +假设字段tid在t1表,t2表中都没有建立索引,那么查找时就不能使用索引了,采用Block Nested-Loop Join算法就是每次从驱动表t1中加载一部分数据行到内存缓冲区Join Buffer 中来,然后对t2表进行全表扫描,扫描时每次拿t2表中的数据行与Join Buffer中的数据进行匹配,匹配完成就添加到结果集。 + 所以全表扫描的次数=驱动表t1的行数/Join Buffer的大小。 +因为Join Buffer是内存缓冲区,在内存中进行元素比较是比较快的,而对t2表进行全表扫描是磁盘Io,是比较慢的,所以应该是尽可能减少全表扫描的次数。所以优化的方式一般是增大Join Buffer的大小,或者是选取数据量小的表作为驱动表,这样可以减少全表扫描的次数,减少磁盘IO。 + +List result = new ArrayList<>(); +//可以把subList理解为每次从t1表中取出,加载到join buufer的那一部分数据 +for( List subList in List t1){ + for(Row r2 in List t2){ + if(subList.contains(r2.tid){ + result.add(r1.join(r2)); + } + } +} +``` + +使用**Block Nested-Loop Join**算法时SQL的EXPLAIN结果extral列是**Using join buffer** + +什么是Join Buffer? +(1)Join Buffer会缓存所有参与查询的列而不是只有Join的列。 +(2)可以通过调整join_buffer_size缓存大小 +(3)join_buffer_size的默认值是256K,join_buffer_size的最大值在MySQL 5.1.22版本前是4G,而之后的版本才能在64位操作系统下申请大于4G的Join Buffer空间。 +(4)使用Block Nested-Loop Join算法需要开启优化器管理配置的optimizer_switch的设置block_nested_loop为on,默认为开启。 + +https://blog.csdn.net/u010841296/article/details/89790399 + +https://mp.weixin.qq.com/s/8W3RzKE1HjKifGzJvzBYzA + +##### 怎么如何优化Join速度? +1.用小结果集驱动大结果集,减少外层循环的数据量: +如果小结果集和大结果集连接的列都是索引列,mysql在内连接时也会选择用小结果集驱动大结果集,因为索引查询的成本是比较固定的,这时候外层的循环越少,join的速度便越快。 +为匹配的条件增加索引:争取使用Index Nested-Loop Join,减少内层表的循环次数 +2.增大join buffer size的大小:当使用BNLJ时,一次缓存的数据越多,那么外层表循环的次数就越少。 +3.减少不必要的字段查询: +(1)当用到BNLJ时,字段越少,join buffer 所缓存的数据就越多,外层表的循环次数就越少; +(2)当用到INLJ时,如果可以不回表查询,即利用到覆盖索引,则可能可以提示速度。(未经验证,只是一个推论) + +4.排序时尽量使用驱动表中的字段 + +因为如果使用的是非驱动表中的字段会对非驱动表(的字段排序)需要对循环查询的合并结果(临时表)进行排序,比较耗时,使用Explain时会发现出现Using temporary。 + +参考文档 +https://www.wengbi.com/thread_99558_1.html +https://www.cnblogs.com/starhu/p/6418842.html +https://www.cnblogs.com/starhu/p/6418833.html + +### Join的工作流程是怎么样的,怎么进行优化? + +##### join的大概使用是怎么样的? + +full outer join 会包含两个表不满足条件的行 + +left join 会包含左边的表不满足条件的行,一般会使用左边的表作为驱动表。 + +right join 会包含右边的表不满足条件的行,一般会使用右边的表作为驱动表。 + +inner join 就是只包含满足条件的行 + +cross join 从表A循环取出每一条记录去表B匹配,cross join 后面不能跟on,只能跟where + +##### exits 和in,join的区别是什么? + +exists是拿外表作为驱动表,外表的数据做循环,每次循环去内表中查询数据,使用适内表比较大的情况 + +例如 + +select * from t1 where t1.tid exists (select t2.tid from t2) + +转换为伪代码为: + +```java +//就是t1作为驱动表 +List result = new ArrayList<>(); +for(Row r1 in List t1){ + for(Row r2 in List t2){ + if(r1.id = r2.tid){ + result.add(r1.join(r2)); + } + } +} +``` + +而 in的话正好相反,是那内表作为驱动表,内表的数据做循环,每次循环去外表查询数据,适合内表比较小的情况。 + +``` +select * from A where cc in (select cc from B) +-- 效率低,用到了A表上cc列的索引; + +select * from A where exists(select cc from B where cc=A.cc) +-- 效率高,用到了B表上cc列的索引。 +``` + +not in 和not exists如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。 + +join的实现其实是先从一个表中找出所有行(或者根据where子句查出符号条件的行),然后去下一个表中循环寻找匹配的行,依次下去,直到找到所有匹配的行,使用join不会去创建临时表,使用in的话会创建临时表,销毁临时表 + +所以不管是in子查询,exists子查询还是join连接查询,底层的实现原理都是一样的,本质上是没有任何区别的,关键的点在关联表的顺序,如果是join连接查询,MySQL会自动调整表之间的关联顺序,选择最好的一种关联方式。和上面in和exists比较的结论一样,小表驱动大表才是最优的选择方式。 + +### MySQL怎么排查使用率低的索引? + +MySQL 5.5以后,有一个`performance_schema`的配置,默认为不开启,开启后,可以统计MySQL数据库的性能进行监测统计,会将内存使用情况,SQL语句使用情况,IO读取情况等等进行统计,并将统计结果写入到performance_schema这个数据库中,这个数据库里面有很多表,记录了性能统计结果。在MySQL bentch中有一个界面会展示出没有使用到索引的结果。 + +例如图中就是我们test数据库中有一个activity表有一个index索引没有使用到。 + +![image-20210204164409753](../static/image-20210204164409753.png) + +这个界面的数据来源也是来自于`performance_schema`这个数据库,里面总共有52个性能统计结果表,其中有一个table_io_waits_summary_by_index_usage表,里面统计了索引使用情况,里面有统计索引在查询,插入,更新,删除语句中使用到的次数。 + +```SQL +SELECT + object_type,//类型,这里是table + object_schema,//索引所在的数据库名 + object_name,//索引所在的表名 + index_name,//索引名称 +COUNT_FETCH,//这个索引在查询语句中使用到的的次数 +COUNT_INSERT,//这个索引在插入语句中使用到的的次数 +COUNT_UPDATE,//这个索引在更新语句中使用到的的次数 +COUNT_DELETE//这个索引在删除语句中使用到的的次数 +FROM + PERFORMANCE_SCHEMA.table_io_waits_summary_by_index_usage; +``` + +这个是查询结果,可以看到idx_ipport这个索引在查询语句中用到了1次。 + +![image-20210204170229799](../static/image-20210204170229799.png) + +table_io_waits_summary_by_index_usage这个表里面还有更详细的统计数据,具体可以看看下面这个链接,里面有介绍 + +参考链接 + +https://www.cnblogs.com/cchust/p/5057498.html + +https://www.cnblogs.com/cchust/p/5061131.html + +### 数据库设计的三大范式是什么? + +1.确保数据库表的每一列的原子性 + +保证每一列的值是原子性的,不可分割的,而不是在查询时需要分割使用函数来查询,降低查询效率。 + +例如说有一个列是存储地址,如果你要根据省份查询,直接从地址中查可能会使用到函数,降低查询效率,最好是单独将省份另外存一份。 + +2.确保数据库表的每一列都与主键相关 + +就是表里面只应该存储一种数据,而不是多种数据,例如订单表就应该存储订单,其中包含商品的id就行了,而不是存储一些商品的具体名称,价格等信息。避免数据冗余,增大维护成本。 + +3.确保数据库表的每一列直接依赖主键,而不是间接依赖主键。 + +这一条其实更第二范式差不多。 + +https://www.cnblogs.com/linjiqin/archive/2012/04/01/2428695.html \ No newline at end of file diff --git a/docs/MySQLOptimize.md b/docs/MySQLOptimize.md new file mode 100644 index 0000000..1cc2260 --- /dev/null +++ b/docs/MySQLOptimize.md @@ -0,0 +1,325 @@ +## 文章说明 +这篇文章主要是记录自己最近在真实工作中遇到的慢查询的案例,然后进行调优分析的过程,欢迎大家一起讨论调优经验。(以下出现的表名,列名都是化名,实际数据也进行过一点微调。可能文章比较贴近实践,已经被51CTO的编辑申请转载了) + + +## 一.复杂的深分页问题优化 + +#### 背景 + +有一个article表,用于存储文章的基本信息的,有文章id,作者id等一些属性,有一个content表,主要用于存储文章的内容,主键是article_id,需求需要将一些满足条件的作者发布的文章导入到另外一个库,所以我同事就在项目中先查询出了符合条件的作者id,然后开启了多个线程,每个线程每次取一个作者id,执行查询和导入工作。 + +查询出作者id是1111,名下的所有文章信息,文章内容相关的信息的SQL如下: + +```SQL +SELECT + a.*, c.* +FROM + article a +LEFT JOIN content c ON a.id = c.article_id +WHERE + a.author_id = 1111 +AND a.create_time < '2020-04-29 00:00:00' +LIMIT 210000,100 +``` + +因为查询的这个数据库是机械硬盘的,在offset查询到20万时,查询时间已经特别长了,运维同事那边直接收到报警,说这个库已经IO阻塞了,已经多次进行主从切换了,我们就去navicat里面试着执行了一下这个语句,也是一直在等待, 然后对数据库执行show proceesslist 命令查看了一下,发现每个查询都是处于Writing to net的状态,没办法只能先把导入的项目暂时下线,然后执行kill命令将当前的查询都杀死进程(因为只是客户端Stop的话,MySQL服务端会继续查询)。 + +然后我们开始分析这条命令执行慢的原因: + +#### 是否是联合索引的问题 + +当前是索引情况如下: + +``` +article表的主键是id,author_id是一个普通索引 +content表的主键是article_id +``` + +所以认为当前是执行流程是先去article表的普通索引author_id里面找到1111的所有文章id,然后根据这些文章id去article表的聚集索引中找到所有的文章,然后拿每个文章id去content表中找文章内容等信息,然后判断create_time是否满足要求,进行过滤,最终找到offset为20000后的100条数据。 + +所以我们就将article的author_id索引改成了联合索引(author_id,create_time),这样联合索引(author_id,create_time)中的B+树就是先安装author_id排序,再按照create_time排序,这样一开始在联合(author_id,create_time)查询出来的文章id就是满足create_time < '2020-04-29 00:00:00'条件的,后面就不用进行过滤了,就不会就是符合就不用对create_time过滤。 + +流程确实是这个流程,但是去查询时,如果limit还是210000, 100时,还是查不出数据,几分钟都没有数据,一直到navica提示超时,使用Explain看的话,确实命中索引了,如果将offset调小,调成6000, 100,勉强可以查出数据,但是需要46s,所以瓶颈不在这里。 + +### 查询慢的原因 + +```SQL +SELECT + a.*, c.* +FROM + article a +LEFT JOIN content c ON a.id = c.article_id +WHERE + a.author_id = 1111 +AND a.create_time < '2020-04-29 00:00:00' +LIMIT 210000,100 +``` +首先我们需要知道innodb引擎在执行时,并不了解我们的业务规则,它是不知道article表中如果有一篇文章存在,那么在content表里面一定会有这篇文章的内容信息,也就是它不知道article表的id在content表中一定会有一个article_id与之对应。所以innodb引擎的执行流程是这样: + +1.先去article表中找出满足`a.author_id = 1111 +AND a.create_time < '2020-04-29 00:00:00'`条件的22000条数据的所有字段,加载到内存中。(在MySQL进行join时,加载到内存中并不只是join字段,而是SELECT 的所有字段,很容易理解,如果只是join的字段,那么最后还需要根据join的字段去回表。) + +2.然后根据这22000数据去content表里面查找文章内容相关的字段。(由于content表存储了文章内容,一些字段是特别大的,是不会存储在聚簇索引的叶子节点中的,而且存储在其他地方,所以会产生大量随机IO,这是导致这个查询这么慢的原因。) + +3.最终把22000条数据返回给MySQL Server,取最后面的100条数据,返回给客户端。 + +使用show table status命令查看article表和content表显示的数据行平均长度 + +| Name | Engine | Row_format | Rows | Avg_Row_length | +| ------- | ------ | ---------- | ------- | -------------- | +| article | InnoDB | Compact | 2682682 | 266 | +| content | InnoDB | Compact | 2824768 | 16847 | + +发现两个表的数据量都是200多万的量级,article表的行平均长度是266,content表的平均长度是16847,简单来说是当 InnoDB 使用 Compact 或者 Redundant 格式存储极长的 VARCHAR 或者 BLOB 这类大对象时,我们并不会直接将所有的内容都存放在数据页节点中,而是将行数据中的前 768 个字节存储在数据页中,后面会通过偏移量指向溢出页。 + +(详细了解可以看看这篇文章[深度好文带你读懂MySQL和InnoDB](https://mp.weixin.qq.com/s?src=11×tamp=1588316993&ver=2311&signature=wlqIQrV2ZK4JJhqP4E1hqr8j3SBaQSEaiPoPM2KlAF9z-*jpWnwYiORweW3LDIWfY2J6LY8coaqXDMFezKZvEIEGRIaMEs5G*0N4naBh9DBCmUjRQnvuluU8Q5LOPttc&new=1)) + +![img](../static/f10940650d2d478a9c71bce1d9a0db3a~tplv-k3u1fbpfcp-zoom-1.image) + +这样再从content表里面查询连续的100行数据时,读取每行数据时,还需要去读溢出页的数据,这样就需要大量随机IO,因为机械硬盘的硬件特性,随机IO会比顺序IO慢很多。所以我们后来又进行了测试, + +只是从article表里面查询limit 200000,100的数据,发现即便存在深分页的问题,查询时间只是0.5s,因为article表的平均列长度是266,所有数据都存在数据页节点中,不存在页溢出,所以都是顺序IO,所以比较快。 + +```SQL +//查询时间0.51s +SELECT a.* FROM article a +WHERE a.author_id = 1111 +AND a.create_time < '2020-04-29 00:00:00' +LIMIT 200100, 100 +``` + +相反的,我们直接先找出100个article_id去content表里面查询数据,发现比较慢,第一次查询时需要3s左右(也就是这些id的文章内容相关的信息都没有过,没有缓存的情况),第二次查询时因为这些溢出页数据已经加载到buffer pool,所以大概0.04s。 + +```SQL +SELECT SQL_NO_CACHE c.* +FROM article_content c +WHERE c.article_id in(100个article_id) +``` + +### 解决方案 + +所以针对这个问题的解决方案主要有两种: + +#### 先查出主键id再inner join + +非连续查询的情况下,也就是我们在查第100页的数据时,不一定查了第99页,也就是允许跳页查询的情况,那么就是使用**先查主键再join**这种方法对我们的业务SQL进行改写成下面这样,下查询出210000, 100时主键id,作为临时表temp_table,将article表与temp_table表进行inner join,查询出中文章相关的信息,并且去left Join content表查询文章内容相关的信息。 第一次查询大概1.11s,后面每次查询大概0.15s + +```SQL +SELECT + a.*, c.* +FROM article a +INNER JOIN( + SELECT id FROM article a + WHERE a.author_id = 1111 + AND a.create_time < '2020-04-29 00:00:00' + LIMIT 210000 , + 100 +) as temp_table ON a.id = temp_table.id +LEFT JOIN content c ON a.id = c.article_id +``` + +#### 优化结果 + +优化前,offset达到20万的量级时,查询时间过长,一直到超时。 + +优化后,offset达到20万的量级时,查询时间为1.11s。 + +#### 利用范围查询条件来限制取出的数据 + +这种方法的大致思路如下,假设要查询test_table中offset为10000的后100条数据,假设我们事先已知第10000条数据的id,值为min_id_value + +`select * from test_table where id > min_id_value order by id limit 0`, 100,就是即利用条件id > min_id_value在扫描索引是跳过10000条记录,然后取100条数据即可,这种处理方式的offset值便成为0了,但此种方式有限制,必须知道offset对应id,然后作为min_id_value,增加id > min_id_value的条件来进行过滤,如果是用于分页查找的话,也就是必须知道上一页的最大的id,所以只能一页一页得查,不能跳页,但是因为我们的业务需求就是每次100条数据,进行分批导数据,所以我们这种场景是可以使用。针对这种方法,我们的业务SQL改写如下: + +```SQL +//先查出最大和最小的id +SELECT min(a.id) as min_id , max(a.id) as max_id +FROM article a +WHERE a.author_id = 1111 +AND a.create_time < '2020-04-29 00:00:00' +//然后每次循环查找 +while(min_id min_id LIMIT 100 + //这100条数据导入完毕后,将100条数据数据中最大的id赋值给min_id,以便导入下100条数据 +} +``` +#### 优化结果 + +优化前,offset达到20万的量级时,查询时间过长,一直到超时。 + +优化后,offset达到20万的量级时,由于知道第20万条数据的id,查询时间为0.34s。 + +## 二.联合索引问题优化 + +联合索引其实有两个作用: +#### 1.充分利用where条件,缩小范围 +例如我们需要查询以下语句: +```SQL +SELECT * FROM test WHERE a = 1 AND b = 2 +``` +如果对字段a建立单列索引,对b建立单列索引,那么在查询时,只能选择走索引a,查询所有a=1的主键id,然后进行回表,在回表的过程中,在聚集索引中读取每一行数据,然后过滤出b = 2结果集,或者走索引b,也是这样的过程。 +如果对a,b建立了联合索引(a,b),那么在查询时,直接在联合索引中先查到a=1的节点,然后根据b=2继续往下查,查出符合条件的结果集,进行回表。 +#### 2.避免回表(此时也叫覆盖索引) +这种情况就是假如我们只查询某几个常用字段,例如查询a和b如下: +```SQL +SELECT a,b FROM test WHERE a = 1 AND b = 2 +``` +对字段a建立单列索引,对b建立单列索引就需要像上面所说的,查到符合条件的主键id集合后需要去聚集索引下回表查询,但是如果我们要查询的字段本身在联合索引中就都包含了,那么就不用回表了。 +#### 3.减少需要回表的数据的行数 +这种情况就是假如我们需要查询a>1并且b=2的数据 +```SQL +SELECT * FROM test WHERE a > 1 AND b = 2 +``` +如果建立的是单列索引a,那么在查询时会在单列索引a中把a>1的主键id全部查找出来然后进行回表。 +如果建立的是联合索引(a,b),基于最左前缀匹配原则,因为a的查询条件是一个范围查找(=或者in之外的查询条件都是范围查找),这样虽然在联合索引中查询时只能命中索引a的部分,b的部分命中不了,只能根据a>1进行查询,但是由于联合索引中每个叶子节点包含b的信息,在查询出所有a>1的主键id时,也会对b=2进行筛选,这样需要回表的主键id就只有a>1并且b=2这部分了,所以回表的数据量会变小。 + +我们业务中碰到的就是第3种情况,我们的业务SQL本来更加复杂,还会join其他表,但是由于优化的瓶颈在于建立联合索引,所以进行了一些简化,下面是简化后的SQL: +```SQL +SELECT + a.id as article_id , + a.title as title , + a.author_id as author_id +from + article a +where + a.create_time between '2020-03-29 03:00:00.003' +and '2020-04-29 03:00:00.003' +and a.status = 1 +``` +我们的需求其实就是从article表中查询出最近一个月,status为1的文章,我们本来就是针对create_time建了单列索引,结果在慢查询日志中发现了这条语句,查询时间需要0.91s左右,所以开始尝试着进行优化。 + +为了便于测试,我们在表中分别对create_time建立了单列索引create_time,对(create_time,status)建立联合索引idx_createTime_status。 + +强制使用idx_createTime进行查询 + +```SQL +SELECT + a.id as article_id , + a.title as title , + a.author_id as author_id +from + article a FORCE INDEX(idx_createTime) +where + a.create_time between '2020-03-22 03:00:00.003' +and '2020-04-22 03:00:00.003' +and a.status = 1 +``` + +强制使用idx_createTime_status进行查询(即使不强制也是会选择这个索引) + +```SQL +SELECT + a.id as article_id , + a.title as title , + a.author_id as author_id +from + article a FORCE INDEX(idx_createTime_status) +where + a.create_time between '2020-03-22 03:00:00.003' +and '2020-04-22 03:00:00.003' +and a.status = 1 +``` + + +#### 优化结果: + +优化前使用idx_createTime单列索引,查询时间为0.91s + +优化前使用idx_createTime_status联合索引,查询时间为0.21s + +#### EXPLAIN的结果如下: + +| id | type | key | key_len | rows | filtered | Extra | +| ---- | ----- | --------------------- | ------- | ------ | -------- | ---------------------------------- | +| 1 | range | idx_createTime | 4 | 311608 | 25.00 | Using index condition; Using where | +| 2 | range | idx_createTime_status | 6 | 310812 | 100.00 | Using index condition | + +#### 原理分析 + +先介绍一下EXPLAIN中Extra列的各种取值的含义 + +#### Using filesort + +当Query 中包含 ORDER BY 操作,而且无法利用索引完成排序操作的时候,MySQL Query Optimizer 不得不选择相应的排序算法来实现。数据较少时从内存排序,否则从磁盘排序。Explain不会显示的告诉客户端用哪种排序。 + +#### Using index + +仅使用索引树中的信息从表中检索列信息,而不需要进行附加搜索来读取实际行(使用二级覆盖索引即可获取数据)。 当查询仅使用作为单个索引的一部分的列时,可以使用此策略。 + +#### Using temporary +要解决查询,MySQL需要创建一个临时表来保存结果。 如果查询包含不同列的GROUP BY和ORDER BY子句,则通常会发生这种情况。官方解释:”为了解决查询,MySQL需要创建一个临时表来容纳结果。典型情况如查询包含可以按不同情况列出列的GROUP BY和ORDER BY子句时。很明显就是通过where条件一次性检索出来的结果集太大了,内存放不下了,只能通过加临时表来辅助处理。 + +#### Using where +表示当where过滤条件中的字段无索引时,MySQL Sever层接收到存储引擎(例如innodb)的结果集后,根据where条件中的条件进行过滤。 + +#### Using index condition +Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行; + + + +我们的实际案例中,其实就是走单个索引idx_createTime时,只能从索引中查出 满足`a.create_time between '2020-03-22 03:00:00.003' and '2020-04-22 03:00:00.003'`条件的主键id,然后进行回表,因为idx_createTime索引中没有status的信息,只能回表后查出所有的主键id对应的行。然后innodb将结果集返回给MySQL Sever,MySQL Sever根据status字段进行过滤,筛选出status为1的字段,所以第一个查询的Explain结果中的Extra才会显示Using where。 + +filtered字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,这个是预估值,因为status取值是null,1,2,3,4,所以这里给的25%。 + +所以第二个查询与第一个查询的区别主要在于一开始去idx_createTime_status查到的结果集就是满足status是1的id,所以去聚集索引下进行回表查询时,扫描的行数会少很多(大概是2.7万行与15万行的区别),之后innodb返回给MySQL Server的数据就是满足条件status是1的结果集(2.7万行),不用再进行筛选了,所以第二个查询才会快这么多,时间是优化前的23%。(两种查询方式的EXPLAIN预估扫描行数都是30万行左右是因为idx_createTime_status只命中了createTime,因为createTime不是查单个值,查的是范围) + +``` +//查询结果行数是15万行左右 +SELECT count(*) from article a +where a.post_time +between '2020-03-22 03:00:00.003' and '2020-04-22 03:00:00.003' + +//查询结果行数是2万6行左右 +SELECT count(*) from article a +where a.post_time +between '2020-03-22 03:00:00.003' and '2020-04-22 03:00:00.003' +and a.audit_status = 1 +``` + +### 发散思考:如果将联合索引(createTime,status)改成(status,createTime)会怎么样? + +``` +where + a.create_time between '2020-03-22 03:00:00.003' +and '2020-04-22 03:00:00.003' +and a.status = 1 +``` +根据最左匹配的原则,因为我们的where查询条件是这样,如果是(createTime,status)那么索引就只能用到createTime,如果是(status,createTime),因为status是查询单个值,所以status,createTime都可以命中,在(status,createTime)索引中扫描行数会减少,但是由于(createTime,status)这个索引本身值包含createTime,status,id三个字段的信息,数据量比较小,而一个数据页是16k,可以存储1000个以上的索引数据节点,而且是查询到createTime后,进行的顺序IO,所以读取比较快,总得的查询时间两者基本是一致。下面是测试结果: + +首先创建了(status,createTime)名叫idx_status_createTime, + +```SQL +SELECT + a.id as article_id , + a.title as title , + a.author_id as author_id +from + article a FORCE INDEX(idx_status_createTime) +where + a.create_time between '2020-03-22 03:00:00.003' +and '2020-04-22 03:00:00.003' +and a.status = 1 +``` + +查询时间是0.21,跟第二种方式(createTime,status)索引的查询时间基本一致。 + +#### Explain结果对比: +| id | type | key | key_len | rows | filtered | Extra | +| ---- | ----- | --------------------- | ------- | ------ | -------- | --------------------- | +| 2 | range | idx_createTime_status | 6 | 310812 | 100.00 | Using index condition | +| 3 | range | idx_status_createTime | 6 | 52542 | 100.00 | Using index condition | + +扫描行数确实会少一些,因为在idx_status_createTime的索引中,一开始根据status = 1排除掉了status取值为其他值的情况。 + +之前建了一个技术交流群,进群可以领取《面试指北》PDF版,希望可以和大家一起学习进步! + +## 干货内容回顾: +### [【大厂面试01期】高并发场景下,如何保证缓存与数据库一致性?](https://mp.weixin.qq.com/s/hwMpAVZ1_p8gLfPAzA8X9w) +### [【大厂面试02期】Redis过期key是怎么样清理的?](https://mp.weixin.qq.com/s/J_nOPKS17Uax2zGrZsE8ZA) +### [【大厂面试03期】MySQL是怎么解决幻读问题的?](https://mp.weixin.qq.com/s/8D6EmZM3m6RiSk0-N5YCww) +### [【大厂面试04期】讲讲一条MySQL更新语句是怎么执行的?](https://mp.weixin.qq.com/s/pNe1vdTT24oEoJS_zs-5jQ) +### [【大厂面试05期】说一说你对MySQL中锁的理解?](https://mp.weixin.qq.com/s/pTpPE33X-iYULYt8DOPp2w) +### [【大厂面试06期】谈一谈你对Redis持久化的理解?](https://mp.weixin.qq.com/s/nff4fd5TnM-CMWb1hQIT9Q) +### [【大厂面试07期】说一说你对synchronized锁的理解?](https://mp.weixin.qq.com/s/H8Cd2fj82qbdLZKBlo-6Dg) +### [【大厂面试08期】谈一谈你对HashMap的理解?](https://mp.weixin.qq.com/s/b4f5NIPl9uVLkRg_UpWSJQ) +![](../static/7795953b44734c0c84b94c78943f88ef~tplv-k3u1fbpfcp-zoom-1.image) + diff --git a/docs/MySQLWork.md b/docs/MySQLWork.md new file mode 100644 index 0000000..b9c29ab --- /dev/null +++ b/docs/MySQLWork.md @@ -0,0 +1,316 @@ +# MySQL慢查询优化(线上案例调优) + + + +## 文章说明 + +这篇文章主要是记录自己最近在真实工作中遇到的慢查询的案例,然后进行调优分析的过程,欢迎大家一起讨论调优经验。(以下出现的表名,列名都是化名,实际数据也进行过一点微调。) + +首发于我的掘金博客:https://juejin.im/editor/posts/5eabd9735188256d8e654e74 + +PS:这篇文章由于写得比较贴近工作实践,已经被51CTO的编辑联系在51CTO公众号进行转载了。 + +## 一.复杂的深分页问题优化 + +#### 背景 + +有一个article表,用于存储文章的基本信息的,有文章id,作者id等一些属性,有一个content表,主要用于存储文章的内容,主键是article_id,需求需要将一些满足条件的作者发布的文章导入到另外一个库,所以我同事就在项目中先查询出了符合条件的作者id,然后开启了多个线程,每个线程每次取一个作者id,执行查询和导入工作。 + +查询出作者id是1111,名下的所有文章信息,文章内容相关的信息的SQL如下: + +```SQL +SELECT + a.*, c.* +FROM + article a +LEFT JOIN content c ON a.id = c.article_id +WHERE + a.author_id = 1111 +AND a.create_time < '2020-04-29 00:00:00' +LIMIT 210000,100 +``` + +因为查询的这个数据库是机械硬盘的,在offset查询到20万时,查询时间已经特别长了,运维同事那边直接收到报警,说这个库已经IO阻塞了,已经多次进行主从切换了,我们就去navicat里面试着执行了一下这个语句,也是一直在等待, 然后对数据库执行show proceesslist 命令查看了一下,发现每个查询都是处于Writing to net的状态,没办法只能先把导入的项目暂时下线,然后执行kill命令将当前的查询都杀死进程(因为只是客户端Stop的话,MySQL服务端会继续查询)。 + +然后我们开始分析这条命令执行慢的原因: + +#### 是否是联合索引的问题 + +当前是索引情况如下: + +``` +article表的主键是id,author_id是一个普通索引 +content表的主键是article_id +``` + +所以认为当前是执行流程是先去article表的普通索引author_id里面找到1111的所有文章id,然后根据这些文章id去article表的聚集索引中找到所有的文章,然后拿每个文章id去content表中找文章内容等信息,然后判断create_time是否满足要求,进行过滤,最终找到offset为20000后的100条数据。 + +所以我们就将article的author_id索引改成了联合索引(author_id,create_time),这样联合索引(author_id,create_time)中的B+树就是先安装author_id排序,再按照create_time排序,这样一开始在联合(author_id,create_time)查询出来的文章id就是满足create_time < '2020-04-29 00:00:00'条件的,后面就不用进行过滤了,就不会就是符合就不用对create_time过滤。 + +流程确实是这个流程,但是去查询时,如果limit还是210000, 100时,还是查不出数据,几分钟都没有数据,一直到navica提示超时,使用Explain看的话,确实命中索引了,如果将offset调小,调成6000, 100,勉强可以查出数据,但是需要46s,所以瓶颈不在这里。 + +真实原因如下: + +先看关于深分页的两个查询,id是主键,val是普通索引 + +#### 直接查询法 + +```SQL +select * from test where val=4 limit 300000,5; +``` +#### 先查主键再join +```SQL +select * from test a +inner join +(select id from test where val=4 limit 300000,5) as b +on a.id=b.id; +``` + +这两个查询的结果都是查询出offset是30000后的5条数据,区别在于第一个查询需要先去普通索引val中查询出300005个id,然后去聚集索引下读取300005个数据页,然后抛弃前面的300000个结果,只返回最后5个结果,过程中会产生了大量的随机I/O。第二个查询一开始在普通索引val下就只会读取后5个id,然后去聚集索引下读取5个数据页。 + +同理我们业务中那条查询其实是更加**复杂**的情况,因为我们业务的那条SQL不仅会读取article表中的210100条结果,而且会每条结果去content表中查询文章相关内容,而这张表有几个TEXT类型的字段,我们使用show table status命令查看表相关的信息发现 + +| Name | Engine | Row_format | Rows | Avg_Row_length | +| ------- | ------ | ---------- | ------- | -------------- | +| article | InnoDB | Compact | 2682682 | 266 | +| content | InnoDB | Compact | 2824768 | 16847 | + +发现两个表的数据量都是200多万的量级,article表的行平均长度是266,content表的平均长度是16847,简单来说是当 InnoDB 使用 Compact 或者 Redundant 格式存储极长的 VARCHAR 或者 BLOB 这类大对象时,我们并不会直接将所有的内容都存放在数据页节点中,而是将行数据中的前 768 个字节存储在数据页中,后面会通过偏移量指向溢出页。 + +(详细了解可以看看这篇文章[深度好文带你读懂MySQL和InnoDB](https://mp.weixin.qq.com/s?src=11×tamp=1588316993&ver=2311&signature=wlqIQrV2ZK4JJhqP4E1hqr8j3SBaQSEaiPoPM2KlAF9z-*jpWnwYiORweW3LDIWfY2J6LY8coaqXDMFezKZvEIEGRIaMEs5G*0N4naBh9DBCmUjRQnvuluU8Q5LOPttc&new=1)) + +![img](../static/171cf4968afd910e.jpeg) + +这样再从content表里面查询连续的100行数据时,读取每行数据时,还需要去读溢出页的数据,这样就需要大量随机IO,因为机械硬盘的硬件特性,随机IO会比顺序IO慢很多。所以我们后来又进行了测试, + +只是从article表里面查询limit 200000,100的数据,发现即便存在深分页的问题,查询时间只是0.5s,因为article表的平均列长度是266,所有数据都存在数据页节点中,不存在页溢出,所以都是顺序IO,所以比较快。 + +```SQL +//查询时间0.51s +SELECT a.* FROM article a +WHERE a.author_id = 1111 +AND a.create_time < '2020-04-29 00:00:00' +LIMIT 200100, 100 +``` + +相反的,我们直接先找出100个article_id去content表里面查询数据,发现比较慢,第一次查询时需要3s左右(也就是这些id的文章内容相关的信息都没有过,没有缓存的情况),第二次查询时因为这些溢出页数据已经加载到buffer pool,所以大概0.04s。 + +```SQL +SELECT SQL_NO_CACHE c.* +FROM article_content c +WHERE c.article_id in(100个article_id) +``` + +### 解决方案 + +所以针对这个问题的解决方案主要有两种: + +#### 先查出主键id再inner join + +非连续查询的情况下,也就是我们在查第100页的数据时,不一定查了第99页,也就是允许跳页查询的情况,那么就是使用**先查主键再join**这种方法对我们的业务SQL进行改写成下面这样,下查询出210000, 100时主键id,作为临时表temp_table,将article表与temp_table表进行inner join,查询出中文章相关的信息,并且去left Join content表查询文章内容相关的信息。 第一次查询大概1.11s,后面每次查询大概0.15s + +```SQL +SELECT + a.*, c.* +FROM article a +INNER JOIN( + SELECT id FROM article a + WHERE a.author_id = 1111 + AND a.create_time < '2020-04-29 00:00:00' + LIMIT 210000 , + 100 +) as temp_table ON a.id = temp_table.id +LEFT JOIN content c ON a.id = c.article_id +``` + +#### 优化结果 + +优化前,offset达到20万的量级时,查询时间过长,一直到超时。 + +优化后,offset达到20万的量级时,查询时间为1.11s。 + +#### 利用范围查询条件来限制取出的数据 + +这种方法的大致思路如下,假设要查询test_table中offset为10000的后100条数据,假设我们事先已知第10000条数据的id,值为min_id_value + +`select * from test_table where id > min_id_value order by id limit 0`, 100,就是即利用条件id > min_id_value在扫描索引是跳过10000条记录,然后取100条数据即可,这种处理方式的offset值便成为0了,但此种方式有限制,必须知道offset对应id,然后作为min_id_value,增加id > min_id_value的条件来进行过滤,如果是用于分页查找的话,也就是必须知道上一页的最大的id,所以只能一页一页得查,不能跳页,但是因为我们的业务需求就是每次100条数据,进行分批导数据,所以我们这种场景是可以使用。针对这种方法,我们的业务SQL改写如下: + +```SQL +//先查出最大和最小的id +SELECT min(a.id) as min_id , max(a.id) as max_id +FROM article a +WHERE a.author_id = 1111 +AND a.create_time < '2020-04-29 00:00:00' +//然后每次循环查找 +while(min_id min_id LIMIT 100 + //这100条数据导入完毕后,将100条数据数据中最大的id赋值给min_id,以便导入下100条数据 +} +``` +#### 优化结果 + +优化前,offset达到20万的量级时,查询时间过长,一直到超时。 + +优化后,offset达到20万的量级时,由于知道第20万条数据的id,查询时间为0.34s。 + +## 二.联合索引问题优化 + +联合索引其实有两个作用: +#### 1.充分利用where条件,缩小范围 +例如我们需要查询以下语句: +```SQL +SELECT * FROM test WHERE a = 1 AND b = 2 +``` +如果对字段a建立单列索引,对b建立单列索引,那么在查询时,只能选择走索引a,查询所有a=1的主键id,然后进行回表,在回表的过程中,在聚集索引中读取每一行数据,然后过滤出b = 2结果集,或者走索引b,也是这样的过程。 +如果对a,b建立了联合索引(a,b),那么在查询时,直接在联合索引中先查到a=1的节点,然后根据b=2继续往下查,查出符合条件的结果集,进行回表。 +#### 2.避免回表(此时也叫覆盖索引) +这种情况就是假如我们只查询某几个常用字段,例如查询a和b如下: +```SQL +SELECT a,b FROM test WHERE a = 1 AND b = 2 +``` +对字段a建立单列索引,对b建立单列索引就需要像上面所说的,查到符合条件的主键id集合后需要去聚集索引下回表查询,但是如果我们要查询的字段本身在联合索引中就都包含了,那么就不用回表了。 +#### 3.减少需要回表的数据的行数 +这种情况就是假如我们需要查询a>1并且b=2的数据 +```SQL +SELECT * FROM test WHERE a > 1 AND b = 2 +``` +如果建立的是单列索引a,那么在查询时会在单列索引a中把a>1的主键id全部查找出来然后进行回表。 +如果建立的是联合索引(a,b),基于最左前缀匹配原则,因为a的查询条件是一个范围查找(=或者in之外的查询条件都是范围查找),这样虽然在联合索引中查询时只能命中索引a的部分,b的部分命中不了,只能根据a>1进行查询,但是由于联合索引中每个叶子节点包含b的信息,在查询出所有a>1的主键id时,也会对b=2进行筛选,这样需要回表的主键id就只有a>1并且b=2这部分了,所以回表的数据量会变小。 + +我们业务中碰到的就是第3种情况,我们的业务SQL本来更加复杂,还会join其他表,但是由于优化的瓶颈在于建立联合索引,所以进行了一些简化,下面是简化后的SQL: +```SQL +SELECT + a.id as article_id , + a.title as title , + a.author_id as author_id +from + article a +where + a.create_time between '2020-03-29 03:00:00.003' +and '2020-04-29 03:00:00.003' +and a.status = 1 +``` +我们的需求其实就是从article表中查询出最近一个月,status为1的文章,我们本来就是针对create_time建了单列索引,结果在慢查询日志中发现了这条语句,查询时间需要0.91s左右,所以开始尝试着进行优化。 + +为了便于测试,我们在表中分别对create_time建立了单列索引create_time,对(create_time,status)建立联合索引idx_createTime_status。 + +强制使用idx_createTime进行查询 + +```SQL +SELECT + a.id as article_id , + a.title as title , + a.author_id as author_id +from + article a FORCE INDEX(idx_createTime) +where + a.create_time between '2020-03-22 03:00:00.003' +and '2020-04-22 03:00:00.003' +and a.status = 1 +``` + +强制使用idx_createTime_status进行查询(即使不强制也是会选择这个索引) + +```SQL +SELECT + a.id as article_id , + a.title as title , + a.author_id as author_id +from + article a FORCE INDEX(idx_createTime_status) +where + a.create_time between '2020-03-22 03:00:00.003' +and '2020-04-22 03:00:00.003' +and a.status = 1 +``` + + +#### 优化结果: + +优化前使用idx_createTime单列索引,查询时间为0.91s + +优化前使用idx_createTime_status联合索引,查询时间为0.21s + +#### EXPLAIN的结果如下: + +| id | type | key | key_len | rows | filtered | Extra | +| ---- | ----- | --------------------- | ------- | ------ | -------- | ---------------------------------- | +| 1 | range | idx_createTime | 4 | 311608 | 25.00 | Using index condition; Using where | +| 2 | range | idx_createTime_status | 6 | 310812 | 100.00 | Using index condition | + +#### 原理分析 + +先介绍一下EXPLAIN中Extra列的各种取值的含义 + +#### Using filesort + +当Query 中包含 ORDER BY 操作,而且无法利用索引完成排序操作的时候,MySQL Query Optimizer 不得不选择相应的排序算法来实现。数据较少时从内存排序,否则从磁盘排序。Explain不会显示的告诉客户端用哪种排序。 + +#### Using index + +仅使用索引树中的信息从表中检索列信息,而不需要进行附加搜索来读取实际行(使用二级覆盖索引即可获取数据)。 当查询仅使用作为单个索引的一部分的列时,可以使用此策略。 + +#### Using temporary +要解决查询,MySQL需要创建一个临时表来保存结果。 如果查询包含不同列的GROUP BY和ORDER BY子句,则通常会发生这种情况。官方解释:”为了解决查询,MySQL需要创建一个临时表来容纳结果。典型情况如查询包含可以按不同情况列出列的GROUP BY和ORDER BY子句时。很明显就是通过where条件一次性检索出来的结果集太大了,内存放不下了,只能通过加临时表来辅助处理。 + +#### Using where +表示当where过滤条件中的字段无索引时,MySQL Sever层接收到存储引擎(例如innodb)的结果集后,根据where条件中的条件进行过滤。 + +#### Using index condition +Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行; + + + +我们的实际案例中,其实就是走单个索引idx_createTime时,只能从索引中查出 满足`a.create_time between '2020-03-22 03:00:00.003' and '2020-04-22 03:00:00.003'`条件的主键id,然后进行回表,因为idx_createTime索引中没有status的信息,只能回表后查出所有的主键id对应的行。然后innodb将结果集返回给MySQL Sever,MySQL Sever根据status字段进行过滤,筛选出status为1的字段,所以第一个查询的Explain结果中的Extra才会显示Using where。 + +filtered字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,这个是预估值,因为status取值是null,1,2,3,4,所以这里给的25%。 + +所以第二个查询与第一个查询的区别主要在于一开始去idx_createTime_status查到的结果集就是满足status是1的id,所以去聚集索引下进行回表查询时,扫描的行数会少很多(大概是2.7万行与15万行的区别),之后innodb返回给MySQL Server的数据就是满足条件status是1的结果集(2.7万行),不用再进行筛选了,所以第二个查询才会快这么多,时间是优化前的23%。(两种查询方式的EXPLAIN预估扫描行数都是30万行左右是因为idx_createTime_status只命中了createTime,因为createTime不是查单个值,查的是范围) + +``` +//查询结果行数是15万行左右 +SELECT count(*) from article a +where a.post_time +between '2020-03-22 03:00:00.003' and '2020-04-22 03:00:00.003' + +//查询结果行数是2万6行左右 +SELECT count(*) from article a +where a.post_time +between '2020-03-22 03:00:00.003' and '2020-04-22 03:00:00.003' +and a.audit_status = 1 +``` + +### 发散思考:如果将联合索引(createTime,status)改成(status,createTime)会怎么样? + +``` +where + a.create_time between '2020-03-22 03:00:00.003' +and '2020-04-22 03:00:00.003' +and a.status = 1 +``` +根据最左匹配的原则,因为我们的where查询条件是这样,如果是(createTime,status)那么索引就只能用到createTime,如果是(status,createTime),因为status是查询单个值,所以status,createTime都可以命中,在(status,createTime)索引中扫描行数会减少,但是由于(createTime,status)这个索引本身值包含createTime,status,id三个字段的信息,数据量比较小,而一个数据页是16k,可以存储1000个以上的索引数据节点,而且是查询到createTime后,进行的顺序IO,所以读取比较快,总得的查询时间两者基本是一致。下面是测试结果: + +首先创建了(status,createTime)名叫idx_status_createTime, + +```SQL +SELECT + a.id as article_id , + a.title as title , + a.author_id as author_id +from + article a FORCE INDEX(idx_status_createTime) +where + a.create_time between '2020-03-22 03:00:00.003' +and '2020-04-22 03:00:00.003' +and a.status = 1 +``` + +查询时间是0.21,跟第二种方式(createTime,status)索引的查询时间基本一致。 + +#### Explain结果对比: +| id | type | key | key_len | rows | filtered | Extra | +| ---- | ----- | --------------------- | ------- | ------ | -------- | --------------------- | +| 2 | range | idx_createTime_status | 6 | 310812 | 100.00 | Using index condition | +| 3 | range | idx_status_createTime | 6 | 52542 | 100.00 | Using index condition | + +扫描行数确实会少一些,因为在idx_status_createTime的索引中,一开始根据status = 1排除掉了status取值为其他值的情况。 \ No newline at end of file diff --git a/docs/Nginx.md b/docs/Nginx.md new file mode 100644 index 0000000..31c2a19 --- /dev/null +++ b/docs/Nginx.md @@ -0,0 +1,381 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +#### [1.nginx负载均衡算法有哪些?](#nginx负载均衡算法有哪些?) +#### [2.一致性hash是什么?](#一致性hash是什么?) +#### [3.有哪些实现限流的技术方案?](#有哪些实现限流的技术方案?) + +### nginx负载均衡算法有哪些? + +##### 1、轮询(默认) + +每个请求按时间顺序逐一分配到不同的后端服务,如果后端某台服务器死机,自动剔除故障系统,使用户访问不受影响。 + +例如: +``` +upstream bakend { + server 192.168.0.1; + server 192.168.0.2; +} +``` +##### 2、weight(根据权值分配) + +weight的值越大分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下。或者仅仅为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。 + +指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 +例如: +``` +upstream bakend { + server 192.168.0.1 weight=10; + server 192.168.0.2 weight=10; +} +``` +##### 3、ip_hash(根据ip地址分配) + +每个请求按访问IP的哈希结果分配,这样来自同一个IP的请求固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题。 + +每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 +例如: +``` +upstream bakend { + ip_hash; + server 192.168.0.1:88; + server 192.168.0.2:80; +} +``` +##### 4、fair(优先分配给响应时间段的服务器) + +比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身不支持fair,如果需要这种调度算法,则必须安装upstream_fair模块。 + +按后端服务器的响应时间来分配请求,响应时间短的优先分配。 +例如: +``` +upstream backend { + server 192.168.0.1:88; + server 192.168.0.2:80; + fair; +} +``` +##### 5、url_hash(根据url分配) + +按访问的URL的哈希结果来分配请求,使每个URL定向到一台后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身不支持url_hash,如果需要这种调度算法,则必须安装Nginx的hash软件包。 + +按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。 + +注意:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法。 + +例如: +``` +upstream backend { + server 192.168.0.1:88; + server 192.168.0.2:80; + hash $request_uri; + hash_method crc32; +} +``` + +### 一致性hash是什么? + +nginx 普通的hash算法支持配置http变量值(例如url或者请求参数)作为hash值计算的key,通过hash计算得出的hash值和总权重的余数作为挑选server的依据。缺点是 + +1.可能对于给后端服务器分配请求时分配得不均匀,有的upstream server负载很低,有的upstream server负载较高。 + +2.增加或者减少upstream server后,所有的请求可能分配的upstream server会发生变化,跟之前不同。 + +所以有了一致性hash,一致性hash就是创建出n个虚拟节点,n个虚拟节点构成一个环,从n个虚拟节点中,挑选出一些节点当成真实的upstream server节点。构成一个每次将计算得到的hash%n,得到请求分配的虚拟节点的位置c,从位置c顺时针移动,获得离c最近的真实upstream server节点。 + +这样请求分配时就会比较均匀,而且upstream server的数量变化只会影响计算出key值hash与其”最近”的预分配的虚拟节点。 + +例如在下面这个图中: + +真实的机器节点是t1,t2,t3。 + +按照一致性hash的规则,请求m4,m3是分配给机器节点t2,请求m2是分配给机器节点t1,请求m1是分配给机器节点t3。 + +一旦有机器节点增加或者减少,只会附近的一个节点。例如如果t2节点被移除了,只会将原本由t2节点处理的请求分配给t1,而其他机器节点不会受影响。 + +![这里写图片描述](../static/SouthEast.png) + +```bash +upstream somestream { + consistent_hash $request_uri; + server 10.50.1.3:11211; + server 10.50.1.4:11211; + server 10.50.1.5:11211; +} +``` + +### 有哪些实现限流的技术方案? + +#### 1.计数器(固定窗口限流+滑动窗口限流) + +固定窗口限流: + +固定窗口算法指每个单位时间相对隔离,一个单位区间的请求量统计跟其他单位区间的请求量统计完全独立。当一个单位时间过期,自动进入下一个时间阶段重新进行计数,固定窗口计数器算法逻辑图如下,固定窗口计数器算法相对简单,但会存在临界问题(用户流量并不会像我们所期望的匀速请求,而是可能在某个时间点集中爆发,在一个窗口快结束的时候来了大量的请求,然后消耗完这个窗口的次数,然后在下个窗口刚开始又来了大量的请求,消耗完这个窗口的次数,这样在这个很短的时间间隔内,处理的请求数会超过>单个窗口的次数限制) + +![img](../static/640.png) + + + +#### 2.滑动窗口限流: + +为了解决固定窗口算法的临界问题,有了滑动窗口限流,这种算法每次统计当前时间往前推一个单位的时间,统计这个单位时间内的请求量,是否超出阀值。(可以使用Redis中的有序集合sorted set来实现,就是每个member存储请求的信息,member的score就是请求发生的时间戳,每次接受请求时,先去查询有序集合的大小,如果数量超出,就删除时间戳过期的(也就是超出当前时间窗口的请求),删除后还是超出,就限流,否则就不限流,并且有一个定时任务定时删除时间戳过期的。) + +但是由于每次都需要统计单位时间的请求量,开销远大于固定窗口算法,所以在真实的业务环境中需要慎重使用滑动窗口算法。 + +![img](../static/640-20200606144302960.png) + +![img](../static/640-1445199.jpeg) + + + +#### 2.令牌桶算法: + +令牌以固定速率产生,并缓存到令牌桶中,令牌桶放满时,多余的令牌被丢弃。请求要获取令牌才能被处理,获取不到令牌时,请求被缓存。(Guava框架中的RateLimiter类就是令牌桶算法的解决方案) + +#### 3.漏桶算法: + +可以认为这种算法有一定固定长度的队列,来缓存请求,消费端每次以固定的速率从队列取出请求进行处理,当队列满了时,请求就只能丢弃。 + +#### 区别: + +漏桶算法和令牌桶算法都是限制数据的平均传输速率,区别在于,令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输。 + +就是假设漏桶每秒钟处理10个请求,使用漏桶法,我们设置桶的大小为10,流出的速率为0.1s流出一个请求,也就是每0.1s能处理一个请求,在第一个0.1s内只处理了1个请求,后面的9个请求都在桶内等待,每过0.1s,处理一个。 + +而使用令牌桶的话,我们会设置每1s向桶中放入10个令牌,假设之前没有请求,桶内有10个令牌,那么来10个请求都可以获得令牌,然后直接进行处理,来第11个请求,如果桶内没有令牌才需要等待。 + +Nginx官方版本限制IP的连接和并发分别有两个模块: + +#### 1.限制请求数 + +- `limit_req_zone` 用来限制单位时间内的请求数,即速率限制,默认采用的漏桶算法 ,也就是超出限制的请求会丢弃,如果设置了burst就会变成令牌桶算法,使用一个长度为burst的队列来存储这些超出限制的请求。(nginx使用的漏桶算法) + + 例子: + + 限制访问速度 + + ```nginx + http { + limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; + server { + location /search/ { + limit_req zone=one burst=5 nodelay; + } + } + ``` + + 这个例子中主要是限制每个IP请求的次数, + + limit_req_zone中的参数 + + * 第一个参数:$binary_remote_addr 表示通过用户请求的ip地址这个标识来做限制,使用binary_remote_addr 而不是remote_addr目的是使用二进制格式的ip地址,可以缩小内存占用量。 + + * 第二个参数:zone=one:10m表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息。 + + * 第三个参数:rate=1r/s表示允许相同标识的客户端的访问频次,这里限制的是每秒1次只允许请求一次,还可以有比如30r/m的。 + + limit_req_zone中的参数 + + - 第二个参数:**burst=5**,burst是爆发的意思,这个配置的意思是设置一个大小为5的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内先进行处理,处理完之后后面的请求还是需要等待生成令牌的速度跟上了之后才能处理。 + +- 第三个参数:**nodelay**,如果没有设置,那么超出缓存区的所有请求会进行排队等待等待,如果设置nodelay,超过访问频次而且缓冲区也满了的时候就会直接返回503。 + + +默认情况下,当某个客户端超过它的限流,NGINX用**503(Service Temporarily Unavailable**状态码来响应。使用***limit_req_status\***指令设置一个不同的状态码,例如limit_req_status 444; + +##### 实战案例 + + ```nginx + limit_req_zone $binary_remote_addr zone=mylimiter:10M rate=2r/s; + server { + location /search { + limit_req_zone=mylimiter; + } + } + ``` + + 这个例子是限制同一ip地址请求/search接口时,限制每秒转发2个请求,也就是500ms转发一个。如果瞬间同一ip来了6个请求,只有第一个请求会成功,后面5个请求会被拒绝,因为nginx的限流统计是基于毫秒的,设置的速度是2r/s,转换一下就是500ms内单个IP只允许通过1个请求,从501ms开始才允许通过第二个请求。(此时也是默认的漏桶的算法) + + ```nginx + limit_req_zone $binary_remote_addr zone=mylimiter:10M rate:2r/s; + server { + location /search { + limit_req_zone=mylimiter burst=4; + } + } + ``` + + 多了一个burst参数,可以认为burst是一个缓冲队列,可以将多余的请求缓存请求,这些请求也不会立即处理,只能等着慢慢被处理。假设同一ip来了6个请求,第一个请求会被立即处理,然后会有4个请求被缓存在队列中,然后每0.5s会从队列中取出一个进行处理。而最后一个请求,由于队列满了,是会被拒绝,返回503。 + + ```nginx + limit_req_zone $binary_remote_addr zone=mylimiter:10M rate:2r/s; + server { + location /search { + limit_req_zone=mylimiter burst=4 nodelay; + } + } + ``` + +多了一个nodelay参数,就是快速转发的意思,不会增加每秒能处理的请求数,但是可以让处于burst队列中请求,就会立即被后台worker处理。假设同一ip来了6个请求,那么第一个请求会被立即处理,然后会有4个请求被缓存在队列中,缓存队列中的请求也是立即被转发处理,第六个请求则是由于队列满了,被拒绝,返回503。 + +https://www.cnblogs.com/CarpenterLee/p/8084533.html + + #### 2.限制并发连接数 + + Nginx 的 **ngx_http_limit_conn_module**模块提供了对资源连接数进行限制的功能。 + + 例如:一次只允许每个IP地址一个连接。 + + ``` + limit_conn_zone $binary_remote_addr zone=addr:10m; + + server { + location /download/ { + limit_conn addr 1; + } + } + ``` + +#### Guava实现限流 + +guava的RateLimiter主要使用令牌桶算法实现限流,可以设置一个令牌生产速率来生产令牌,然后调用acquire()来获得令牌,当调用时桶内令牌不够可以进行预支,并且下一个请求调用acquire()方法时需要等待令牌还完之后才能获得令牌。 + +```java +public static void main(String[] args) { + RateLimiter limiter = RateLimiter.create(1); + for(int i = 1; i < 10; i = i + 1 ) { + double waitTime = limiter.acquire(10); + System.out.println("cutTime=" + System.currentTimeMillis() + " acq:" + i + " waitTime:" + waitTime); + } +} +``` + +例如每秒生产1个令牌,第一次一次性获得10个令牌,等待时间是0s,后面下一次获得令牌时需要将10个令牌全部还完才能获得令牌,也就是需要等待10s左右。 + +输出如下: + +```java +cutTime=1591437767990 acq:1 waitTime:0.0 +cutTime=1591437777994 acq:2 waitTime:9.99607 +cutTime=1591437787991 acq:3 waitTime:9.994006 +``` + +参考链接: + +[死磕nginx系列--nginx 限流配置](https://www.cnblogs.com/biglittleant/p/8979915.html) + +[字节跳动工作总结:高并发系统中的限流算法](https://mp.weixin.qq.com/s?src=11×tamp=1591424755&ver=2383&signature=sEx-P1P2FbqGV-QSWPw3TRzOkCAp26w7rTjU2y6ab49GRumif0GpPOn2XclKx3SqC7*zNrRMvVVaEkpwXqcWZnFA6ouIPzg5mo1e-Mnp7vNKLm1ZroOMfoBlNX3wkHvc&new=1) + +### RateLimiter深度使用 + +使用RateLimiter完成简单的大流量限流,抢购秒杀限流。 +RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率。 +通常可应用于抢购限流防止冲垮系统;限制某接口、服务单位时间内的访问量,譬如一些第三方服务会对用户访问量进行限制;限制网速,单位时间内只允许上传下载多少字节等。 +下面来看一些简单的实践demo,需要先引入guava的maven依赖。 +Demo1.有很多任务,但希望每秒不超过N个 + +```java + /** + * + * 有很多个任务,但希望每秒不超过X个,可用此类 + */ + public class Demo1 { + public static void main(String[] args) { + //0.5代表一秒最多多少个 + RateLimiter rateLimiter = RateLimiter.create(0.5); + List tasks = new ArrayList(); + for (int i = 0; i < 10; i++) { + tasks.add(new UserRequest(i)); + } + ExecutorService threadPool = Executors.newCachedThreadPool(); + for (Runnable runnable : tasks) { + System.out.println("等待时间:" + rateLimiter.acquire()); + threadPool.execute(runnable); + } + } + + private static class UserRequest implements Runnable { + private int id; + + public UserRequest(int id) { + this.id = id; + } + + public void run() { + System.out.println(id); + } + } +} +``` + +Demo2.抢购场景限流 +如我们预估数据库能承受并发10,超过了可能会造成故障,我们就可以对该请求接口进行限流。 + +```java + @RestController + public class IndexController { + @Resource(name = "db") + private GoodInfoService goodInfoService; + + RateLimiter rateLimiter = RateLimiter.create(10); + + @RequestMapping("/miaosha") + public Object miaosha(int count, String code) { + System.out.println("等待时间" + rateLimiter.acquire()); + if (goodInfoService.update(code, count) > 0) { + return "购买成功"; + } + return "购买失败"; + } + @RequestMapping("/add") + public Object add() { + for (int i = 0; i < 100; i++) { + GoodInfo goodInfo = new GoodInfo(); + goodInfo.setCode("iphone" + i); + goodInfo.setAmount(100); + goodInfoService.add(goodInfo); + } + return "添加成功"; + } + } +``` + +Demo3.抢购场景降级 + +```java + /** + * tryAcquire(long timeout, TimeUnit unit) + * 从RateLimiter 获取许可如果该许可可以在不超过timeout的时间内获取得到的话, + * 或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待) + */ + @RequestMapping("/buy") + public Object miao(int count, String code) { + //判断能否在1秒内得到令牌,如果不能则立即返回false,不会阻塞程序 + if (!rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) { + System.out.println("短期无法获取令牌,真不幸,排队也瞎排"); + return "失败"; + } + if (goodInfoService.update(code, count) > 0) { + System.out.println("购买成功"); + return "成功"; + } + System.out.println("数据不足,失败"); + return "失败"; + } +``` + +### rediscell的使用 + + Redis 4.0 版本中提供的 Redis-Cell 模块,该模块使用的是漏斗算法,并且提供了原子的限流指令,而且依靠 Redis 这个天生的分布式程序就可以实现比较完美的限流了。Redis-Cell 实现限流的方法也很简单,只需要使用一条指令 cl.throttle 即可,使用示例如下: + +``` +> cl.throttle mylimit 15 30 60 +1)(integer)0 # 0 表示获取成功,1 表示拒绝 +2)(integer)15 # 漏斗容量 +3)(integer)14 # 漏斗剩余容量 +4)(integer)-1 # 被拒绝之后,多长时间之后再试(单位:秒)-1 表示无需重试 +5)(integer)2 # 多久之后漏斗完全空出来 +``` + + + diff --git a/docs/RedisBasic.md b/docs/RedisBasic.md new file mode 100644 index 0000000..e5634f2 --- /dev/null +++ b/docs/RedisBasic.md @@ -0,0 +1,367 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + + + +下面是主要是自己看完《Redis设计与实现》,《Redis深度历险:核心原理与应用实践》后,为了更好得掌握Redis,网上找了一些面试题,查阅书籍和资料后,写的解答。 + +#### [1.Redis是什么?](#Redis是什么?) + +#### [2.Redis过期key是怎么样清理的?](#Redis过期key是怎么样清理的?) +#### [3.Redis为什么是单线程的?](#Redis为什么是单线程的?) +#### [4.Redis的性能为什么这么高?](#Redis的性能为什么这么高?) +#### [5.Linux中IO模型一共有哪些?](#Linux中IO模型一共有哪些?) +#### [6.同步与异步的区别是什么?](#同步与异步的区别是什么?) +#### [7.阻塞与非阻塞的区别是什么?](#阻塞与非阻塞的区别是什么?) +#### [8.如何解决Redis缓存穿透问题?](#如何解决Redis缓存穿透问题?) +#### [9.如何解决Redis缓存击穿问题?](#如何解决Redis缓存击穿问题?) +#### [10.如何解决Redis缓存雪崩问题?](#如何解决Redis缓存雪崩问题?) +#### [11.如何解决缓存与数据库的数据一致性问题?](#如何解决缓存与数据库的数据一致性问题?) + + + + +### Redis是什么? +Redis是一个开源的,基于内存的,也可进行持久化的,使用C语言编写的键值对存储数据库。 + +### Redis过期key是怎么样清理的? + +##### (1)惰性清除 + +在访问key时,如果发现key已经过期,那么会将key删除。 + +##### (2)定时清理 + +Redis配置项hz定义了serverCron任务的执行周期,默认每次清理时间为25ms,每次清理会依次遍历所有DB,从db的expires字典(里面保存了设置了过期时间的键值对,key就是指向键对象,value是过期时间)中随机取出20个key,如果过期就删除,如果其中有5个key过期,说明过期率超过了25%,那么就继续对这个db进行清理,否则开始清理下一个db。 + +##### (3)内存不够时清理 + +当执行写入命令时,如果发现内存不够,那么就会按照配置的淘汰策略清理内存,淘汰策略一般有6种,Redis4.0版本后又增加了2种,主要由分为三类 + +* 第一类 不处理,等报错(默认的配置) + + * noeviction,发现内存不够时,不删除key,执行写入命令时发现内存不够直接返回错误信息。(Redis默认的配置就是noeviction) + +* 第二类 从所有结果集中的key中挑选,进行淘汰(随机,lru,lfu三种) + + * allkeys-random 就是从所有的key中随机挑选key,进行淘汰 + * allkeys-lru 就是从所有的key中挑选最近使用时间距离现在最远的key,进行淘汰 + * allkeys-lfu 就是从所有的key中挑选使用频率最低的key,进行淘汰。(这是Redis 4.0版本后新增的策略) + +* 第三类 从设置了过期时间的key中挑选,进行淘汰(随机,lru,ttl,lfu) + + 这种就是从设置了expires过期时间的结果集中选出一部分key淘汰,挑选的算法有: + + * volatile-random 从设置了过期时间的结果集中随机挑选key删除。 + * volatile-lru 从设置了过期时间的结果集中挑选上次使用时间距离现在最久的key开始删除 + * volatile-ttl 从设置了过期时间的结果集中挑选可存活时间最短的key开始删除(也就是从哪些快要过期的key中先删除) + + * volatile-lfu 从过期时间的结果集中选择使用频率最低的key开始删除(这是Redis 4.0版本后新增的策略) + +##### LRU算法 +LRU算法的设计原则是如果一个数据近期没有被访问到,那么之后一段时间都不会被访问到。所以当元素个数达到限制的值时,优先移除距离上次使用时间最久的元素。 + +可以使用双向链表Node+HashMap来实现,每次访问元素后,将元素移动到链表头部,当元素满了时,将链表尾部的元素移除,HashMap主要用于根据key获得Node以及添加时判断节点是否已存在和删除时快速找到节点。 + +PS:使用单向链表能不能实现呢,也可以,单向链表的节点虽然获取不到pre节点的信息,但是可以将下一个节点的key和value设置在当前节点上,然后把当前节点的next指针指向下下个节点,这样相当于把下一个节点删除了 + +```java +//双向链表 + public static class ListNode { + String key;//这里存储key便于元素满时,删除尾节点时可以快速从HashMap删除键值对 + Integer value; + ListNode pre = null; + ListNode next = null; + ListNode(String key, Integer value) { + this.key = key; + this.value = value; + } + } + + ListNode head; + ListNode last; + int limit=4; + + HashMap hashMap = new HashMap(); + + public void add(String key, Integer val) { + ListNode existNode = hashMap.get(key); + if (existNode!=null) { + //从链表中删除这个元素 + ListNode pre = existNode.pre; + ListNode next = existNode.next; + if (pre!=null) { + pre.next = next; + } + if (next!=null) { + next.pre = pre; + } + //更新尾节点 + if (last==existNode) { + last = existNode.pre; + } + //移动到最前面 + head.pre = existNode; + existNode.next = head; + head = existNode; + //更新值 + existNode.value = val; + } else { + //达到限制,先删除尾节点 + if (hashMap.size() == limit) { + ListNode deleteNode = last; + hashMap.remove(deleteNode.key); + //正是因为需要删除,所以才需要每个ListNode保存key + last = deleteNode.pre; + deleteNode.pre = null; + last.next = null; + } + ListNode node = new ListNode(key,val); + hashMap.put(key,node); + if (head==null) { + head = node; + last = node; + } else { + //插入头结点 + node.next = head; + head.pre = node; + head = node; + } + } + + } + + public ListNode get(String key) { + return hashMap.get(key); + } + + public void remove(String key) { + ListNode deleteNode = hashMap.get(key); + ListNode preNode = deleteNode.pre; + ListNode nextNode = deleteNode.next; + if (preNode!=null) { + preNode.next = nextNode; + } + if (nextNode!=null) { + nextNode.pre = preNode; + } + if (head==deleteNode) { + head = nextNode; + } + if (last == deleteNode) { + last = preNode; + } + hashMap.remove(key); + } +``` + +##### LFU算法 +LFU算法的设计原则时,如果一个数据在最近一段时间被访问的时次数越多,那么之后被访问的概率会越大,实现是每个数据都有一个引用计数,每次数据被访问后,引用计数加1,需要淘汰数据时,淘汰引用计数最小的数据。在Redis的实现中,每次key被访问后,引用计数是加一个介于0到1之间的数p,并且访问越频繁p值越大,而且在一定的时间间隔内,如果key没有被访问,引用计数会减少。 + +### Redis为什么是单线程的? +Redis官方FAQ回答: + +Redis是基于内存的操作,读取数据很快,不需要在某个线程读取数据时,切换到另一个线程来执行来提高CPU利用率,所以CPU不会成为瓶颈所在,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。 +(这里的单线程指的是处理客户端发送的请求命令的文件处理器模块是单线程,其他模块不一定是单线程的。从Redis 4.0版本后,Redis又逐渐引入了多线程。) + +##### Redis采用单线程的优势: + +1.Redis项目的代码会更加清晰,处理逻辑会更加简单。 + +2.不用考虑多个线程修改数据的情况,修改数据时不用加锁,解锁,也不会出现死锁的问题,导致性能消耗。 + +3.不存在多进程或者多线程导致的切换而造成的一些性能消耗。 + +##### Redis采用单线程的劣势: + +1.无法充分发挥多核机器的优势,不过可以通过在机器上启动多个Redis实例来利用资源。(但是启动多个Redis实例可能会导致在进行AOF重写时,竞争IO资源,导致磁盘写入压力过大,所以可以使用脚本来触发实例的AOF重写,让所有实例的 AOF 串行执行。) + +### Redis的性能为什么这么高? +根据官网的介绍,Redis单机可以到到10W的QPS(每秒处理请求数),Redis这么快的原因主要有以下几点: + +1.完全基于内存,数据全部存储在内存中,内存读取时没有磁盘IO,所以速度非常快。 + +2.Redis采用单线程的模型,没有上下文切换的开销,也没有竞态条件,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗。 + +3.Redis项目中使用的数据结构都是专门设计的,例如SDS(简单动态字符串)是对C语言中的字符串频繁修改时,会频繁地进行内存分配,十分消耗性能,而SDS会使用空间预分配和惰性空间释放来避免这些问题的出现。 +空间预分配技术: 对SDS进行修改时,如果修改后SDS实际使用长度为len, + +当len<1M时,分配的空间会是2*len+1,也就是会预留len长度的未使用空间,其中1存储空字符 + +当len>1M时,分配的空间会是len+1+1M,也就是会预留1M长度的未使用空间,其中1存储空字符 + +4.采用多路复用IO模型,可以同时监测多个流的IO事件能力,在空闲时,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态唤醒,轮询那些真正发出了事件的流,并只依次顺序的处理就绪的流。可以让单个线程高效的处理多个连接请求(尽量减少网络 I/O 的时间消耗)。 + +### Linux中IO模型一共有哪些? + +IO模型主要由阻塞式I/O模型,非阻塞式I/O模型,I/O复用模型,信息驱动式I/O模型,异步I/O模型。 +##### 阻塞式I/O模型(也就是BIO,Blocking IO) + +![img](../static/ABUIABAEGAAg0MKrwAUosJqimgYwgAU4xQI.png) + +用户态进程发出一个IO请求时,会调用recvfrom系统调用去获取数据,如果当前内核中数据没有准备好,那么会让出CPU时间片,一直阻塞等待,不会进行其他操作。直到内核中的数据准备好,将数据拷贝到用户空间内存,然后recvfrom返回成功的信号,此时用户态进行才解除阻塞的状态,处理收到的数据。 + +##### 非阻塞时I/O模型 + +![img](../static/ABUIABAEGAAg7MKrwAUogfiO-QUwgAU48gI.png) + +在非阻塞式I/O模型中,当进程等待内核的数据,而当该数据未到达的时候,进程会不断询问内核,直到内核准备好数据。 +用户态进程调用recvfrom接收数据,当前并没有数据报文产生,此时recvfrom返回EWOULDBLOCK,用户态进程会一直调用recvfrom询问内核,待内核准备好数据的时候,之后用户态进程不再询问内核,待数据从内核复制到用户空间,recvfrom成功返回,用户态进程开始处理数据。 + +##### I/O多路复用模型 + +![img](../static/ABUIABAEGAAg_sKrwAUojMPt1gIwgAU4mgM.png) + +I/O复用指的是多个I/O连接复用一个进程。 + +**基础版的I/O复用模型** + +最初级的I/O复用,就是**一个进程**对应**多个Socket**连接,每次从头至尾进行遍历,判断是否有I/O事件准备好了,需要处理,有的话就进行处理,缺点是效率比较低,如果一直没有事件进来,会导致CPU空转。 + +**升级版的I/O复用模型:** + +**select和poll** + +当没有I/O事件时,进程处于阻塞状态,当有I/O事件时,就会有一个代理去唤醒进程,对所有的文件描述符进行轮询(一个文件描述符对应一个Socket连接),来处理I/O事件。(这里的代理也就是select和poll,select只能观察1024个连接,poll可以观察无限个连接,因为poll是基于链表来实现的) + +**epoll** + +epoll是对select和poll的升级版,解决了很多问题,是线程安全的,而且可以通知进程是哪个Socket连接有I/O事件,不需要进行全部连接进行遍历,提高了查找效率。 + +epoll和select/poll最大区别是 + +(1)epoll内部使用了mmap共享了用户和内核的部分空间,避免了数据的来回拷贝。 +(2)epoll基于事件驱动,epoll_wait只返回发生的事件避免了像select和poll对事件的整个轮寻操作(时间复杂度为O(N)),epoll时间复杂度为O(1)。 + +##### 信息驱动式I/O模型 + +![img](../static/ABUIABAEGAAglcOrwAUoqPiNiQYwgAU4lgM.png) + +是非阻塞的,当需要等待数据时,用户态进程会给内核发送一个信号,告知自己需要的数据,然后就去执行其他任务了,内核准备好数据后,会给用户态发送一个信号,用户态进程收到之后,会立马调用recvfrom,等待数据从从内核空间复制到用户空间,待完成之后recvfrom返回成功指示,用户态进程才处理数据。 + +##### 异步I/O模型 +与信息驱动式I/O模型区别在于,是在数据从内核态拷贝到用户空间之后,内核才通知用户态进程来处理数据。在复制数据到用户空间这个时间段内,用户态进程也是不阻塞的。 + +参考链接: + +http://blog.csdn.net/lzb348110175/article/details/98941378 + +https://xie.infoq.cn/article/b3816e9fe3ac77684b4f29348 + +### epoll水平触发和边缘触发的区别? + +**水平触发**和**边缘触发**两种: + +- LT,默认的模式(水平触发) 只要该fd还有数据可读,每次 `epoll_wait` 都会返回它的事件,提醒用户程序去操作, +- ET是“高速”模式(边缘触发) + + 只会提示一次,直到下次再有数据流入之前都不会再提示,无论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读完,即读到read返回值小于请求值或遇到EAGAIN错误 + +epoll使用“事件”的就绪通知方式,通过`epoll_ctl`注册fd,一旦该fd就绪,内核就会采用类似回调机制激活该fd,`epoll_wait`便可收到通知。 + +### 同步与异步的区别是什么? +同步与异步的区别在于调用结果的通知方式上。 +同步执行一个方法后,需要等待结果返回轮询调用结果才能继续执行,然后继续执行下去。 +异步执行一个方法后,不会等待结果的返回,被调用方在执行完成后通过回调来通知调用方继续执行。 + +### 阻塞与非阻塞的区别是什么? +阻塞与非阻塞的区别在于进程/线程在等待消息时,进程/线程是否是挂起状态。 + +##### 阻塞调用 +在消息发出去后,消息返回之前,当前进程/线程会被挂起,直到有消息返回,当前进/线程才会被激活。 +##### 非阻塞调用 + +在消息发出去后,不会阻塞当前进/线程,而会立即返回,可以去执行其他任务。 + +### BIO,NIO,AIO有什么区别? + +一次IO的读操作分为等待就绪和IO操作两个阶段,等待就绪就是等待TCP RecvBuffer里面的数据就绪好,也就是发送方的数据全部发送到网卡里面来,操作就是CPU将数据从网卡拷贝到用户空间。 + +**BIO(同步阻塞型)** + +以Java中的IO方式为例,BIO就是同步阻塞型的IO,当一个Socket连接发送数据过来以后,需要为这个Socket连接创建一个线程,由线程调用Socket的read()方法读取数据,需要先把数据从网卡拷贝到内核空间,再拷贝到用户态的内存空间,在数据读取完成前,线程都是阻塞的。这种IO方式就是比较消耗资源,假设有1000个活跃的Socket连接,需要创建出1000个线程来读取数据,读取时都是阻塞的,每个线程有自己独有的线程栈,默认大小是1M。 + +**NIO(同步非阻塞型)** + +nio就是多路io复用,就是一个线程来处理多个Socket连接,节省线程资源。以select为例,就是有一个长度为1024的数组,每个元素对应一个Socket连接,线程轮询这个数据,判断哪个Socket连接的数据是出于就绪状态,此时就将数据拷贝到用户空间然后进行处理。由于IO操作阶段是需要等待数据拷贝到用户空间完成才能返回,所以是同步的。由于每次判断内核中Socket缓冲区的数据是否就绪的函数是直接返回的,如果就绪就返回有数据,不是就绪就返回0,线程不需要阻塞等待,所以是非阻塞的。 + +**AIO(异步非阻塞型)** + +AIO就是NIO的升级版,在数据处于就绪状态时,也是异步将Socket缓冲区中的数据拷贝到用户空间,然后执行异步回调函数,所以在IO操作阶段也是异步的。 + + + +BIO里用户最关心“我要读”,NIO里用户最关心"我什么时候可以读了",在AIO模型里用户更需要关注的是“什么时候读完了,我可以直接进行处理了”。 + +https://zhuanlan.zhihu.com/p/23488863 + +https://www.cnblogs.com/sxkgeek/p/9488703.html + +https://blog.csdn.net/weixin_34378045/article/details/91930797 + +### 如何解决Redis缓存穿透问题? +Redis 缓存穿透指的是攻击者故意大量请求一些Redis缓存中不存在key的数据,导致请 +求打到数据库上,导致数据库压力过大。 + +##### 解决方案如下: + +1.做好参数校验,无效的请求直接返回,只能避免一部分情况,攻击者总是可以找到一些没有覆盖的情况。 + +2.对缓存中找不到的key,需要去数据库查找的key,缓存到Redis中,但是可能会导致Redis中缓存大量无效的key,可以设置一个很短的过期时间,例如1分钟。 + +3.也可以使用布隆过滤器,将所有可能的存在的数据通过去hash值的方式存入到一个足够大的bitmap中去,处理请求时,通过在bitmap中查找,可以将不存在的数据拦截掉。 + +### 如何解决Redis缓存击穿问题? + +缓存击穿主要指的是某个热点key失效,导致大量请求全部转向数据库,导致数据库压力过大。 + +##### 解决方案: + +1.对热点key设置永不过期。 + +2.加互斥锁,缓存中没有热点key对应的数据时,等待100ms,由获得锁的线程去读取数据库然后设置缓存。 + +### 如何解决Redis缓存雪崩问题? + +缓存雪崩主要指的是短时间内大量key失效,导致所有请求全部转向数据库,导致数据库压力过大。 + +##### 解决方案: + +1.在给缓存设置失效时间时加一个随机值,避免集体失效。 + +2.双缓存机制,缓存A的失效时间为20分钟,缓存B的失效时间会比A长一些,从缓存A读取数据,缓存A中没有时,去缓存B中读取数据,并且启动一个异步线程来更新缓存A(如果已经有异步线程正在更新了,就不用重复更新了)。以及更新缓存B,以便延迟B的过期时间。 + +### 如何解决缓存与数据库的数据一致性问题? + +首先需要明白会导致缓存与数据库的数据不一致的几个诱因: +多个写请求的执行顺序不同导致脏数据。 +更新时正好有读请求,读请求取到旧数据然后更新上。或者数据库是读写分离的,在主库更新完之后,需要一定的时间,从库才能更新。 + +##### 1.先更新数据库,后更新缓存 +1.两个写请求,写请求A,写请求B,A先更新数据库,B后更新数据库,但是可能B会先更新缓存,A后更新缓存,这样就会导致缓存里面的是旧数据。 +2.更新缓存时失败也有可能导致缓存是旧数据。 + +##### 2.先删除缓存,在更新数据库 +1.删除缓存后,更新数据库之前假如正好有读请求,读请求把旧数据设置到缓存了。 +##### 3.先更新数据库,再删除缓存 +1.更新后删除缓存时,网络不好,删除失败也有可能导致缓存是旧数据。 + +2.写请求A更新数据库然后删除缓存,读请求B读数据时发现缓存中没有数据,就从数据库拿到旧数据准备设置到缓存上去时,如果整个架构是读写分离的,写请求更新数据是在主库上更新,读请求B是去从库上读数据,如果此时主库上的新数据还没有同步到从库,这样读请求B在从库读到旧数据,设置到缓存上,导致缓存里面是旧数据。 + +#### 正确的方案 + +##### 1.写请求串行化 + +将写请求更新之前先获取分布式锁,获得之后才能更新,这样实现写请求的串行化,但是会导致效率变低。 +##### 2.先更新数据库,异步删除缓存,删除失败后重试 + + 先更新数据库,异步删除缓存,删除缓存失败时,继续异步重试,或者将操作放到消息队列中,再进行删除操作。(如果数据库是读写分离的,那么删除缓存时需要延迟删除,否则可能会在删除缓存时,从库还没有收到更新后的数据,其他读请求就去从库读到旧数据然后设置到缓存中。) + +![image](../static/o_update1.png) + +##### 3.业务项目更新数据库,其他项目订阅binlog更新 + +业务项目直接更新数据库,然后其他项目订阅binlog,接收到更新数据库操作的消息后,更新缓存,更新缓存失败时,新建异步线程去重试或者将操作发到消息队列,然后后续进行处理。但是这种方案更新mysql后还是有一定延迟,缓冲中才是新值。 + +![image](../static/o_update2-20200419221055438.png) + +参考资料:https://www.cnblogs.com/rjzheng/p/9041659.html \ No newline at end of file diff --git a/docs/RedisBook1.md b/docs/RedisBook1.md new file mode 100644 index 0000000..c47c320 --- /dev/null +++ b/docs/RedisBook1.md @@ -0,0 +1,877 @@ +##客官,这是一份精心编写的《Redis设计与实现》读书心得(上篇) + +### 第一部分 数据结构与对象 + +#### 第二章 简单动态字符串 +Redis没有使用C语言的char数组的方式来存储字符串,而是自己定义了一个简单动态字符串结构体类型SDS(simple dynamic string), +``` +struct sdshdr { + int len;//字符串长度 + int free;//剩余使用空间,也就是buf数组中未使用的字节数 + char buf[];//字节数组,用于保存字符串 +} +``` +![image.png](../static/12609483-c914918f6dfecfc5.png) + +在上面例子中,使用SDS保存了字符串Redis,SDS的情况如下: +·free属性的值为0,表示这个SDS没有分配任何未使用空间。 +·len属性的值为5,表示这个SDS保存了一个五字节长的字符串。 +·buf属性是一个char类型的数组,数组的前五个字节分别保存了'R'、'e'、'd'、'i'、's'五个字符,而最后一个字节则保存了空字符'\0'。”遵从结尾使用空字符这一惯例的好处是,SDS可以直接重用一部分C字符串函数库里面的函数。 +##### SDS 与 C字符串的区别 +1.可以使用常数级别的时间复杂度获取字符串长度 +因为SDS结构体使用len变量保存长度,而C语言字符串需要遍历字符数组获取长度 +2.防止缓冲区溢出 +因为C语言字符串不记录自身长度,拼接时是直接将字符串拼接在字符串后面,所以后面如果有其他字符串的话,会把其他字符串的内容覆盖。如果后面是不可写的内存,则会报错。如果SDS,拼接时会判断长度,长度不够会进行扩容。 +3.减少修改字符串带来的内存重分配次数 +对于C语言字符串,底层是一个N+1的字符数组,是用多少,分配多少,所以字符串的拼接和截断都会导致内存重分配。 + +对于SDS,进行扩容时,会多分配空间以减少内存重分配的次数。 +当使用长度len<1MB时,分配的总空间为len+len+1,也就是剩余空间为与使用长度相同,再加上额外1字节保存空字符 +当使用长度len>1MB时,分配的总空间为len+1MB+1,也就是剩余空间为1MB,再加上额外1字节保存空字符 + +SDS进行字符串截断时,会延迟字符串剩余空间的释放时机 +字符串进行截断后,程序并不立即进行内存重分配来回收多余的字节,而是使用free变量进行记录,将空间闲置,便于以后使用,当然也可以显式调用函数对空间进行释放 + +4.SDS可以保存二进制数据 +C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。 + +5.兼容部分C字符串函数 +因为遵循C字符串以空字符结尾的惯例,所以可以使用部分C字符串函数 + +#####第3章 链表 +因为C语言没有提供链表的实现,所以Redis自己实现了链表。 +//链表的数据结构 +``` +typedef struct list { + listNode *head;//表头指针 + listNode *tail;//表尾指针 + unsigned long len;//总节点数量 + void *(*dup) (void *ptr);//节点值复制函数,复制节点保存的值 + void (*free)(void *ptr);//节点值释放函数,释放节点保存的值 + int (*match)(void *ptr, void *key);//节点值对比函数,用与比较节点值与输入值 +}list +``` +//链表节点的数据结构 +``` +typedef struct listNode { + struct listNode *prev;//指向前一个节点的指针 + struct listNode *next;//指向后一个节点的指针 + void *value;//节点保存的值 +}listNode +``` +这是一个包含三个节点的链表 +![image.png](../static/12609483-71bd98f13bbe8fa9.png) +Redis中的链表具备以下特点: +双端:每个节点保存了指向前后两个节点的指针 +无环:表头节点的prev为NULL,表尾节点的next为NULL +有表头指针和表尾指针 +记录了链表长度 +多态:链表节点listNode使用指针void *保存节点的值,而链表list使用dup、free和match指针来根据链表中存放的对象的不同从而实现不同的方法。 + +###字典 +字典是一种用于保存键值对的抽象数据结构。因为C语言没有提供字典的实现,所以Redis使用哈希表实现了字典这种数据结构。 +下图为一个空的哈希表示意图: +![image.png](../static/12609483-659c79a0d288942a.png) +下图是普通状态(非rehash期间)下,保存了两个键值对的字典: +![image.png](../static/12609483-376f1967e3784961.png) + +哈希表节点的数据结构如下: +``` +typedef struct dictEntry { + //键 + void *key; + //值,可以是一个指针,也可以是一个uint64_t整数,也可以是int64_t的整数 + union { + void *val; + uint64_tu64; + int64_ts64; + } v; + //指向下一个节点的指针 + struct dictEntry *next; +} dictEntry; +``` + +Redis使用的哈希表数据结构如下: +``` +typedef struct dictht { + //哈希表数组,每个元素存储的是哈希表节点链表 + dictEntry **table; + //哈希表大小,也就是table数组的大小 + unsigned long size; + //哈希表大小掩码,总是等于size-1,用于计算哈希表节点在table中存储的位置 + usigned long sizemask; + //当前存储的节点总数 + unsigned long used; +} dictht; +``` + +Redis字典的数据结构如下: +``` +typedef struct dict { + //类型特定函数,type是一个指向dictType结构的指针,每个dictType保存了一系列用于操作特定类型键值对的函数,不同的字典会有不同的类型特定函数 + dictType *type; + //私有数据,privatedata保存了一些需要传给类型特定函数的可选参数 + void *privatedata; + //哈希表数组,ht是一个数组,保存了两个哈希表,一般情况下,字典只使用ht[0]哈希表,ht[1]哈希表只会在对ht[0]哈希表进行rehash时使用。 + dictht ht[2]; + //rehash索引,当不进行rehash时,值为-1 + int rehashindex; +} +``` + +类型特定函数的数据结构 +``` +typedef struct dictType { +    //  +计算哈希值的函数 +    unsigned int (*hashFunction)(const void *key); +    //  +复制键的函数 +    void *(*keyDup)(void *privdata, const void *key); +    //  +复制值的函数 +    void *(*valDup)(void *privdata, const void *obj); +    //  +对比键的函数 +    int (*keyCompare)(void *privdata, const void *key1, const void *key2); +    //  +销毁键的函数 +    void (*keyDestructor)(void *privdata, void *key); +    //  +销毁值的函数 +    void (*valDestructor)(void *privdata, void *obj); +} dictType; +``` +##### 哈希算法 +当要将一个新的键值对添加到字典里面时,Redis先根据键值对的键计算出哈希值, +``` +hash = dict->type->hashFunction(key); +``` +然后根据哈希值计算得到索引值 +``` +index = hash & dict->ht[x].sizemask; +``` +再根据索引值,将包含新键值对的哈希表节点放到哈希表数组的指定索引上面。 +##### 解决键冲突 +Redis的哈希表使用链地址法(separate chaining)来解决键冲突,每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表连接起来,这就解决了键冲突的问题。 +##### rehash(扩容和收缩) +为了让哈希表的负载因子(load factor)维持在一个合理的范围之内,当哈希表保存的键值对数量太多或者太少时,程序需要对哈希表的大小进行相应的扩展或者收缩。 +扩展和收缩哈希表的工作可以通过执行rehash(重新散列)操作来完成,Redis对字典的哈希表执行rehash的步骤如下: +1.为字典的ht[1]哈希表分配空间,这个哈希表的空间大小取决于要执行的操作,以及ht[0]当前包含的键值对数量(也即是ht[0].used属性的值): +(1) 如果执行的是扩展操作,那么ht[1]的大小为第一个大于等于ht[0].used*2的2的n次方幂; +(2) 如果执行的是收缩操作,那么ht[1]的大小为第一个大于等于ht[0].used的2的n次方幂; +2.将保存在ht[0]中的所有键值对rehash到ht[1]上面:rehash指的是重新计算键的哈希值和索引值,然后将键值对放置到ht[1]哈希表的指定位置上。 +3.当ht[0]包含的所有键值对都迁移到了ht[1]之后(ht[0]变为空表),释放ht[0],将ht[1]设置为ht[0],并在ht[1]新创建一个空白哈希表,为下一次rehash做准备。 +##### 哈希表扩展和收缩的触发条件 +扩展操作: +1.服务器目前没有在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于1。 +2.服务器目前正在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于5。 +Redis需要创建当前服务器进程的子进程,而大多数操作系统都采用写时复制(copy-on-write)技术来优化子进程的使用效率,所以在子进程存在期间,服务器会提高执行扩展操作所需的负载因子,从而尽可能地避免在子进程存在期间进行哈希表扩展操作,这可以避免不必要的内存写入操作,最大限度地节约内存。 +收缩操作: +当哈希表的负载因子小于0.1时,程序自动开始对哈希表执行收缩操作。 +##### 渐进式rehash +当键值对比较多时,如果要一次性完成rehash那么会对性能产生影响,所以可以分多次,渐进式地完成rehash操作。 +哈希表渐进式rehash的详细步骤: +1)为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两个哈希表。 +2)在字典中维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash工作正式开始。 +3)在rehash进行期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],当rehash工作完成之后,程序将rehashidx属性的值增一。 +4)随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有键值对都会被rehash至ht[1],这时程序将rehashidx属性的值设为-1,表示rehash操作已完成。 +渐进式rehash的好处在于它采取分而治之的方式,将rehash键值对所需的计算工作均摊到对字典的每个添加、删除、查找和更新操作上,从而避免了集中式rehash而带来的庞大计算量。 + +##第5章 跳跃表 +跳跃表是一种有序数据结构,通过在每个节点中维持多个指向它3前面节点的指针,来达到快速访问节点的目的。Redis在两个地方用到了跳跃表,一个是在实现有序集合键时,当一个有序集合的元素数量比较多或者元素的成员是比较长的字符串时,会采用跳跃表作为有序集合键的底层实现。一个是在集群节点中用作内部数据结构。 +![image.png](../static/12609483-78e1877dec1728ca.png) +上图是一个跳跃表,最左边的是zskiplist结构,用于保存跳跃表相关的信息,包含以下属性: + +``` +typedef struct zskiplist { +    // 表头节点和表尾节点 +    structz skiplistNode *header, *tail; +    // 表中节点的数量,头结点不计算在内 +    unsigned long length; +    // 表中层数最大的节点的层数 +    int level; +} zskiplist; + +``` +位于zskiplist结构右方的是四个zskiplistNode结构,该结构包含以下属性: +``` +typedef struct zskiplistNode { +    // 层,level是一个数组,在创建跳跃表节点的时候,程序都根据幂次定律(power law,越大的数出现的概率越小)随机生成一个介于1和32之间的值作为level数组的大小,这个大小就是层的“高度” +    struct zskiplistLevel { +        // 前进指针:用于访问位于表尾方向的其他节点 +        struct zskiplistNode *forward; +        // 跨度:当前节点和前进指针所指向节点的距离 +        unsigned int span; +    } level[]; +    // 后退指针:指向位于当前节点的前一个节点。后退指针在程序从表尾向表头遍历时使用。 +    struct zskiplistNode *backward; +    // 分值,跳跃表所有节点都是按照分值从小到大排序的,分值相同时,则按照成员对象在字典序中的大小进行排序 +    double score; +    // 成员对象,每个节点所保存的成员对象都是唯一的 +    robj *obj; +} zskiplistNode; +``` +跳跃表遍历过程: +![](../static/12609483-2c54436d6108d7f1.png) +1)迭代程序首先访问跳跃表的第一个节点(表头),然后从第四层的前进指针移动到表中的第二个节点。 +2)在第二个节点时,程序沿着第二层的前进指针移动到表中的第三个节点。 +3)在第三个节点时,程序同样沿着第二层的前进指针移动到表中的第四个节点。 +4)当程序再次沿着第四个节点的前进指针移动时,它碰到一个NULL,程序知道这时已经到达了跳跃表的表尾,于是结束这次遍历。 + +## 第6章 整数集合 +当一个集合只包含整数值元素时,Redis就会使用整数集合作为集合键的底层实现。 +整数集合的数据结构 +``` +typedef struct intset { + //编码方式,决定contents数组的类型,encoding的值是INTSET_ENC_INT16,那么代表contents数组的类型是int16_t类型 + uint32_t encoding; +//集合元素的个数,也就是contents数组的长度 + uint32_t length; +//contents数组会按照从小到大的顺序来存储集合元素 + int8_t contents[]; +}intset; +``` +下图是一个包含五个int16_t类型整数值的整数集合 +![](../static/12609483-71e11f3b05fdd87b.png) + +##### 升级 +当我们将一个新元素添加到整数集合里面时,如果新元素的类型比整数集合的contents数组的类型要大时,会对集合进行升级。 +升级步骤: +1)根据新元素的类型,扩展整数集合底层数组的空间大小,并为新元素分配空间。 +2)将底层数组现有的所有元素都转换成与新元素相同的类型,并将类型转换后的元素放置到正确的位上,而且在放置元素的过程中,需要继续维持底层数组的有序性质不变,升级后的元素在数组中存储的位置也是按照从小到大排序的。 +3)将新元素添加到底层数组里面。 +##### 升级的好处 +1.提升灵活性。 +因为C语言是静态类型语言,为了避免类型错误,我们通常不会将两种不同类型的值放在同一个数据结构里面。整数集合可以通过自动升级底层数组来适应新元素,所以我们可以随意地将int16_t、int32_t或者int64_t类型的整数添加到集合中,而不必担心出现类型错误,这种做法非常灵活。 +2.节约内存 +要让一个数组可以同时保存int16_t、int32_t、int64_t三种类型的值,最简单的做法就是直接使用int64_t类型的数组作为整数集合的底层实现。不过这样一来,即使添加到整数集合里面的都是int16_t类型或者int32_t类型的值,数组都需要使用int64_t类型的空间去保存它们,从而出现浪费内存的情况。而整数集合现在的做法既可以让集合能同时保存三种不同类型的值,又可以确保升级操作只会在有需要的时候进行,这可以尽量节省内存。 +##### 降级 +整数集合不支持降级操作,一旦对数组进行了升级,编码就会一直保持升级后的状态。 + +## 第7章 压缩列表 +压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值,它是列表键和哈希键的底层实现之一。 +列表 +当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。 +哈希表 +当一个哈希键只包含少量键值对,比且每个键值对的键和值要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做哈希键的底层实现。 +##### 压缩列表构成 +![image.png](../static/12609483-b142de351caa4bdc.png) +下图展示了一个包含三个节点的压缩列表 +列表zlbytes属性的值为0x50(十进制80),表示压缩列表的总长为80字节。 +列表zltail属性的值为0x3c(十进制60),这表示如果我们有一个指向压缩列表起始地址的指针p,那么只要用指针p加上偏移量60,就可以计算出表尾节点entry3的地址。 +列表zllen属性的值为0x3(十进制3),表示压缩列表包含三个节点。 +![](../static/12609483-6453143eae73ed6f.png) + +###### 压缩表节点构成 +每个压缩列表节点都由previous_entry_length、encoding、content三个部分组成,每个压缩列表节点可以保存一个字节数组或者一个整数值,其中,字节数组可以是以下三种长度的其中一种: +·长度<=63(2^6–1)字节的字节数组; +·长度<=16383(2^14–1)字节的字节数组; +·长度<=4294967295(2^32–1)字节的字节数组; +而整数值则可以是以下六种长度的其中一种: +·4位长,介于0至12之间的无符号整数; +·1字节长的有符号整数; +·3字节长的有符号整数; +·int16_t类型整数; +·int32_t类型整数; +·int64_t类型整数。 +![](../static/12609483-f802533e7bf1b7f7.png) + +##### previous_entry_length +因为节点的previous_entry_length属性记录了前一个节点的长度,所以程序可以通过指针运算,根据当前节点的起始地址来计算出前一个节点的起始地址。 +·如果前一节点的长度<=254字节,那么previous_entry_length属性的长度为1字节:前一节点的长度就保存在这一个字节里面。 +·如果前一节点的长度>=254字节,那么previous_entry_length属性的长度为5字节:其中属性的第一字节会被设置为0xFE(十进制值254),而之后的四个字节则用于保存前一节点的长度。 +![image.png](../static/12609483-36e827ab2773002a.png) +图7-5展示了一个包含一字节长previous_entry_length属性的压缩列表节点,属性的值为0x05,表示前一节点的长度为5字节。 +图7-6展示了一个包含五字节长previous_entry_length属性的压缩节点,属性的值为0xFE00002766,其中值的最高位字节0xFE表示这是一个五字节长的previous_entry_length属性,而之后的四字节0x00002766(十进制值10086)才是前一节点的实际长度。 + +##### encoding +节点的encoding属性记录了节点的content属性所保存数据的类型以及长度: +·一字节、两字节或者五字节长,值的最高位为00、01或者10的是字节数组编码:这种编码表示节点的content属性保存着字节数组,数组的长度由编码除去最高两位之后的其他位记录; +·一字节长,值的最高位以11开头的是整数编码:这种编码表示节点的content属性保存着整数值,整数值的类型和长度由编码除去最高两位之后的其他位记录; +下图中表7-2记录了所有可用的字节数组编码,而表7-3则记录了所有可用的整数编码” +![image.png](../static/12609483-b0ccf7f7ee0ed9cb.png) + +##### content +节点的content属性负责保存节点的值,节点值可以是一个字节数组或者整数,值的类型和长度由节点的encoding属性决定。 +下图中展示了一个保存字节数组的节点示例: +·编码的最高两位00表示节点保存的是一个字节数组; +·编码的后六位001011记录了字节数组的长度11; +·content属性保存着节点的值"hello world"。 +![](../static/1b7102166835857ff8e2597db27e970b.png) +下图中展示了一个保存整数值的节点示例 +![image.png](../static/12609483-fea86f8efeff63c0.png) + +##### 连锁更新 +前面说过,每个节点的previous_entry_length属性都记录了前一个节点的长度: +·如果前一节点的长度小于254字节,那么previous_entry_length属性需要用1字节长的空间来保存这个长度值。 +·如果前一节点的长度大于等于254字节,那么previous_entry_length属性需要用5字节长的空间来保存这个长度值。 +现在,考虑这样一种情况:在一个压缩列表中,有多个连续的、长度介于250字节到253字节之间的节点e1至eN,因为e1至eN的所有节点的长度都小于254字节,所以记录这些节点的长度只需要1字节长的previous_entry_length属性,换句话说,e1至eN的所有节点的previous_entry_length属性都是1字节长的。 +这时,如果我们将一个长度大于等于254字节的新节点new设置为压缩列表的表头节点,那么new将成为e1的前置节点,因为e1的previous_entry_length属性仅长1字节,它没办法保存新节点new的长度,所以程序将对压缩列表执行空间重分配操作,并将e1节点的previous_entry_length属性从原来的1字节长扩展为5字节长。 +现在,麻烦的事情来了,e1原本的长度介于250字节至253字节之间,在为previous_entry_length属性新增四个字节的空间之后,e1的长度有可能变成了介于254字节至257字节之间,这样的话,如果要让e2的previous_entry_length属性可以记录下e1的长度,程序需要再次对压缩列表执行空间重分配操作。“正如扩展e1引发了对e2的扩展一样,扩展e2也会引发对e3的扩展,而扩展e3又会引发对e4的扩展……为了让每个节点的previous_entry_length属性都符合压缩列表对节点的要求,程序需要不断地对压缩列表执行空间重分配操作,直到eN为止。 +Redis将这种在特殊情况下产生的连续多次空间扩展操作称之为“连锁更新”(cascade update) +除了添加新节点可能会引发连锁更新之外,删除节点也可能会引发连锁更新。” +![image.png](../static/12609483-4f9566693b78aa1c.png) +因为连锁更新在最坏情况下需要对压缩列表执行N次空间重分配操作,而每次空间重分配的最坏复杂度为O(N),所以连锁更新的最坏复杂度为O(N^2)。 +要注意的是,尽管连锁更新的复杂度较高,但它真正造成性能问题的几率是很低的: +·首先,压缩列表里要恰好有多个连续的、长度介于250字节至253字节之间的节点,连锁更新才有可能被引发,在实际中,这种情况并不多见; +·其次,即使出现连锁更新,但只要被更新的节点数量不多,就不会对性能造成任何影响:比如说,对三五个节点进行连锁更新是绝对不会影响性能的; +因为以上原因,ziplistPush等命令的平均复杂度仅为O(N),在实际中,我们可以放心地使用这些函数,而不必担心连锁更新会影响压缩列表的性能。 + +## 第8章 对象 +Redis基于C语言实现了简单动态字符串,双端链表,字典,压缩列表,整数集合等数据结构,基于这些数据结构实现了五种对象,字符串对象,列表对象,哈希对象,集合对象,有序集合对象。 +一个Redis对象至少包含type,encoding,ptr三个属性 +``` +typedef struct redisObject { + //类型,区分对象是五种对象中的哪一张 + unsigned type:4; + //编码 + unsigned encoding:4; + //指向底层实现数据结构的指针 + void *ptr; + // ... +} +``` +##### 类型 +type取值范围如下: +![image.png](../static/12609483-ab82c3317f8d0b3c.png) + +##### 编码和底层实现 +Redis可以根据使用场景,对每种Redis对象使用不同底层数据结构来做为实现,而使用哪种数据结构作为底层实现用encoding属性标识 +编码对应表如下: +![image.png](../static/12609483-2bbc4ac66f3fc019.png) + +##### 字符串对象 +字符串对象的编码可以是int,raw或者embstr,具体如下图所示: +![image.png](../static/12609483-0569068844757572.png) + +int +当字符串对象保存的是一个整数值,并且可以使用long类型表示,那么字符串对象就会把整数值保存在字符串对象的 ptr 属性里面。这样做的优点是: + 1、节省内存 + 2、对于整数值的字符串对象可能会被执行INCR操作,SDS需要先将字符串转成整形,在执行加减操作,再将结果转成字符串保存如果底层保存一个整形变量就不需要做类型转换了(将void*转换成long) +![image.png](../static/12609483-ef708d9fdeca1857.png) + +raw +如果字符串对象保存的是一个字符串,并且字符串值的长度大于32,那么字符串对象将使用一个简单动态字符串来保存这个字符串值,并将对象的编码设置为raw。 +下图是一个使用raw编码的字符串对象 +![image.png](../static/12609483-ac3e3e5a7b3b427e.png) + +embstr +embstr编码是一种专门用于保存短字符串的优化编码方式,这种编码和raw编码一样,都是使用redisObject和sdshdr结构来表示字符串对象,但是raw编码会调用两次内存分配函数来分别创建redisObject和sdshdr结构,而embstr编码是通过调用一次内存分配函数来分配一块连续的空间。除此以外,因为所有数据都保存在一块连续的内存里面,可以更好利用缓存带来的优势。 + +![embstr](../static/12609483-293bd833c87dda4c.png) + +最后,如果是浮点数,在Redis中也是使用字符串来进行表示的,在有需要进行数值计算时,会将字符串转换为浮点数,然后进行计算。 + +##### 列表对象 +列表对象的底层实现可以是ziplist或者linkedlist,当列表对象保存的字符串长度小于64字节时且元素个数小于512个时,列表对象会使用ziplist编码来实现,否则会使用linkedlist来实现。(这两个上限值可以通过配置参数修改) +下图是保存了1,"three",5三个元素的列表对象,使用ziplist实现, +![image.png](../static/12609483-66ad206714156a51.png) + +下图是保存了1,"three",5三个元素的列表对象,使用linkedlist实现,linkedlist的双端链表结构包含了多个字符串对象,字符串对象是五种类型中唯一一种会被其他四种类型对象嵌套的对象。 +![image.png](../static/12609483-55942da80ed331dc.png) + +##### 哈希对象 +哈希对象的底层实现可以是ziplist或者hashtable,当哈希对象保存的字符串长度小于64字节时且元素个数小于512个时,哈希对象会使用ziplist编码来实现,否则会使用hashtable来实现。(这两个上限值可以通过配置参数修改) +当使用ziplist实现的列表对象时,当有新的键值对要加入到哈希表时,Redis会先将键推入压缩列表表尾,然后再将值加入压缩列表表尾,保证同一键值对的两个节点紧挨在一起。如下图所示: +![image.png](../static/12609483-79f894260bd7a035-9624539.png) +![](../static/12609483-ee0a70ad12f8437c.png) +当使用hashtable实现哈希对象时,哈希对象中的每个键值对都是使用一个字典键值对来保存,字典的每个键和值都是一个字符串对象,如下图所示: +![image.png](../static/12609483-6d3ccb46dfb9e99b.png) + +##### 集合对象 +集合对象的底层实现可以是inset或者hashtable,当集合对象保存的都是整数值且元素个数小于512个时,集合对象会使用inset编码来实现,否则会使用hashtable来实现。(这两个上限值可以通过配置参数修改) +inset编码的集合对象使用整数集合作为底层实现,所有元素都保存在整数集合里面。如下图所示: +![image.png](../static/12609483-25a4fa2be26ec351.png) +hashtable编码的结婚对象使用字典作为底层实现,字典的每个键是一个字符串对象,保存集元素,字典的值则全部被设置为NULL。如下图所示: +![image.png](../static/12609483-43f76e0c7cc3f4d5.png) + +##### 有序集合对象 +有序集合对象的底层实现可以是ziplist或者skiplist,当有序集合对象保存的元素长度都小于64字节且元素个数小于512个时,集合对象会使用ziplist编码来实现,否则会使用skiplist来实现。(这两个上限值可以通过配置参数修改) +ziplist +当有序集合对象使用ziplist作为底层实现时,每个集合元素使用两个挨在一起的压缩列表节点报错,第一个节点保存元素的成员,第二个节点保存元素的分值,压缩列表内按分值从小到大排序,分值较小的放置在靠近表头的位置,分值较大的放置在靠近表尾的方向。如下图所示: +![image.png](../static/12609483-90c7eac6c6d06828-9624547.png) +![image.png](../static/12609483-b1fe8217cff50300.png) +skiplist +skiplist编码的有序集合对象使用zset结构作为底层实现,一个在set结构同时包含一个字典和一个跳跃表: + +``` +typedef struct zset { + zskiplist *zsl; + dict *dict; +} zset; +``` +zset结构中的zsl跳跃表按分值从小到大保存了所有集合元素,每个跳跃表节点保存了一个集合元素,跳跃表节点object属性保存了元素的成员,score属性保存了分值。可以以O(NlogN)的复杂度来实现范围型查找操作 +zset结构中的dict字典为有序结合创建了一个从成员到分支的映射,字典的键保存了元素的成员,值保存了元素的分值。 +有序集合之所以采用字典和跳跃表两个数据结构的原因是字典可以以O(1)复杂度查找成员的分值,而跳跃表可以以O(NlogN)的复杂度来实现范围型查找操作,缺一不可。 +有序集合如下图所示: +![image.png](../static/12609483-27d2db1766354b36.png) + +![image.png](../static/12609483-45e8e5dfa59aa8d3.png) + +##### 对象共享 +当一个键已经创建了一个整数值的字符串对象时,后续其他键也需要这个整数值的字符串对象时,不会重新创建一个新的整数值的字符串对象,而是将字符串对象的引用计数加一,两个键一起使用这些共享对象。Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值) +这些共享对象不单单只有字符串键可以使用,那些在数据结构中嵌套了字符串对象的对象(linkedlist编码的列表对象、hashtable编码的哈希对象、hashtable编码的集合对象,以及zset编码的有序集合对象)都可以使用这些共享对象。 +目前对象共享只对保存整数值的字符串对象有效,因为当服务器考虑将一个共享对象设置为键的值对象时,程序需要先检查给定的共享对象和键想创建的目标对象是否完全相同,只有在共享对象和目标对象完全相同的情况下,程序才会将共享对象用作键的值对象,而一个共享对象保存的值越复杂,验证共享对象和目标对象是否相同所需的复杂度就会越高,消耗的CPU时间也会越多。 +如果共享对象是保存整数值的字符串对象,那么验证操作的复杂度为O(1); +如果共享对象是保存字符串值的字符串对象,那么验证操作的复杂度为O(N); +如果共享对象是包含了多个值(或者对象的)对象,比如列表对象或者哈希对象,那么验证操作的复杂度将会是O(N 2)。 +##### 对象空转时长 +除了前面介绍过的type、encoding、ptr和refcount四个属性之外,redisObject结构包含的最后一个属性为lru属性,该属性记录了对象最后一次被命令程序访问的时间。 +键的空转时长主要用于内存回收,如果服务器打开了maxmemory选项,并且服务器用于回收内存的算法为volatile-lru或者allkeys-lru,那么当服务器占用的内存数超过了maxmemory选项所设置的上限值时,空转时长较高的那部分键会优先被服务器释放,从而回收内存。 + + +## 第9章 数据库 +Redis是一个键值对数据库服务器,服务器默认会创建16个数据库。可以使用SELECT命令进行数据库切换,例如 SELECT 2切换到数据2号数据库。 +数据库键空间 +每个数据库都使用redisDb接个进行表示 +``` +typedef struct redisDb { + dict *dict; +} +``` +dict保存了数据库中的所有键值对,每个键都是字符串对象,每个值可以是字符串对象,列表对象,哈希表对象,集合对象,有序集合对象。 +下图是数据库键空间例子, +·alphabet是一个列表键,键的名字是一个包含字符串"alphabet"的字符串对象,键的值则是一个包含三个元素的列表对象。 +·book是一个哈希表键,键的名字是一个包含字符串"book"的字符串对象,键的值则是一个包含三个键值对的哈希表对象。 +·message是一个字符串键,键的名字是一个包含字符串"message"的字符串对象,键的值则是一个包含字符串"hello world"的字符串对象。 +![image.png](../static/12609483-3b8557ed89a19b8a.png) + +##### 读写键空间的维护操作 +(1)读写一个建时,会根据键是否存在来更新键空间命中次数和不命中次数 +(2)如果读写的键存在,那么会更新建的LRU(最近一次的使用时间) +(3)如果读写的键已过期,会先删除这个过期键,然后执行其他操作 +(4)如果有客户端使用WATCH命令监视了某个键,那么服务器在对被监视的键进行修改之后,会将这个键标记为脏(dirty),从而让事务程序注意到这个键已经被修改过 +(5)服务器每次修改一个键之后,都会对脏(dirty)键计数器的值增1,这个计数器会触发服务器的持久化以及复制操作 +(6)如果服务器开启了数据库通知功能,那么在对键进行修改之后,服务器将按配置发送相应的数据库通知 +##### 设置过期时间 +通过EXPIRE命令或者PEXPIRE命令,客户端可以以秒或者毫秒精度为数据库中的某个键设置生存时间(Time To Live,TTL) +redisDb结构的expires字典保存了数据库中所有键的过期时间,过期字典的键是一个指针,这个指针指向键空间中的某个键对象(也即是某个数据库键)。过期字典的值是一个long long类型的整数,这个整数保存了键所指向的数据库键的过期时间——一个毫秒精度的UNIX时间戳。 +下图展示了一个带有过期字典的数据库例子: +![](../static/70b12d74e6433967e7d8d115425ae028.png) +第一个键值对的键为alphabet键对象,值为1385877600000,这表示数据库键alphabet的过期时间为1385877600000(2013年12月1日零时)。 +·第二个键值对的键为book键对象,值为1388556000000,这表示数据库键book的过期时间为1388556000000(2014年1月1日零时)。 + +##### 其他命令 +使用PERSIST命令可以移除一个键的过期时间。 +``` +redis> PEXPIREAT message 1391234400000 +(integer) 1 +redis> TTL message +(integer) 13893281 +redis> PERSIST message +(integer) 1 +redis> TTL message +(integer) -1 +``` +TTL命令以秒为单位返回键的剩余生存时间,而PTTL命令则以毫秒为单位返回键的剩余生存时间 +``` +redis> PEXPIREAT alphabet 1385877600000 +(integer) 1 +redis> TTL alphabet +(integer) 8549007 +redis> PTTL alphabet +(integer) 8549001011 +``` +##### 过期键删除策略 +定时删除:设置键过期时间时创建定时器,键过期时,定时器会执行键删除操作。优点是节约内存,缺点是会占用很多CPU时间,创建一个定时器需要用到时间事件,而查找事件的复杂度为O(N) +惰性删除:每次获取键时,判断是否过期,如果过期进行删除。缺点是浪费内存。 +定期删除:每个一段时间,检查数据库,删除里面的过期键。 +Redis的过期键删除策略是采用惰性删除和定期删除相结合的方式,每次读写键时会判断键是否过期,如果过期会对键进行删除。并且会定期在一个规定时间内,多次遍历各个数据库,从expires字典中随机检查一定数量的键的过期时间,如果过期会对键进行删除。(默认会检查16个数据库,对每个数据库随机检查20个键) +##### AOF、RDB和复制功能对过期键的处理 +生成RDB文件 +在执行SAVE命令或者BGSAVE命令创建一个新的RDB文件时,Redis会对键进行检查,过期的键不会被保存到新创建的RDB文件中去。 +载入RDB文件 +主服务器:不会载入过期的建 +从服务器:会载入所有键,包含过期的键 +因为主从服务器进行数据同步时,从服务器的数据库就会被清空 +AOF文件写入 +当服务器以AOF持久化模式运行时,如果数据库中的某个键已经过期,但它还没有被惰性删除或者定期删除,那么AOF文件不会因为这个过期键而产生任何影响。 +当过期键被惰性删除或者定期删除之后,程序会向AOF文件追加(append)一条DEL命令,来显式地记录该键已被删除。 +AOF重写 +和生成RDB文件时类似,在执行AOF重写的过程中,程序会对数据库中。 +复制 +当服务器运行在复制模式下时,从服务器的过期键删除动作由主服务器控制: +·主服务器在删除一个过期键之后,会显式地向所有从服务器发送一个DEL命令,告知从服务器删除这个过期键。 +·从服务器在执行客户端发送的读命令时,即使碰到过期键也不会将过期键删除,而是继续像处理未过期的键一样来处理过期键。 +·从服务器只有在接到主服务器发来的DEL命令之后,才会删除过期键。 +通过由主服务器来控制从服务器统一地删除过期键,可以保证主从服务器数据的一致性,也正是因为这个原因,当一个过期键仍然存在于主服务器的数据库时,这个过期键在从服务器里的复制品也会继续存在。 +##### 数据库通知 +键空间通知主要监听某个键执行哪些命令。 +以下代码展示了客户端如何获取0号数据库中针对message键执行的所有命令,根据发回的通知显示,先后共有SET、EXPIRE、DEL三个命令对键message进行了操作。 +``` +127.0.0.1:6379> SUBSCRIBE _ _keyspace@0_ _:message +Reading messages... (press Ctrl-C to quit) +1) "subscribe"  // 订阅信息 +2) "__keyspace@0__:message"      +3) (integer) 1   +1) "message"    //执行SET命令 +2) "_ _keyspace@0_ _:message"    +3) "set"         +1) "message"    //执行EXPIRE命令 +2) "_ _keyspace@0_ _:message"    +3) "expire"      +1) "message"    //执行DEL命令 +2) "_ _keyspace@0_ _:message"    +3) "del"         +``` +键事件通知主要是监听某个命令被哪些键执行了。 +以下是一个键事件通知的例子,代码展示了客户端如何获取0号数据库中所有执行DEL命令的键,根据发回的通知显示,key、number、message三个键先后执行了DEL命令。 +``` +127.0.0.1:6379> SUBSCRIBE _ _keyevent@0_ _:del +Reading messages... (press Ctrl-C to quit) +1) "subscribe"  // 订阅信息 +2) "_ _keyevent@0_ _:del" +3) (integer) 1 +1) "message"    //键key执行了DEL命令 +2) "_ _keyevent@0_ _:del" +3) "key" +1) "message"    //键number执行了DEL命令 +2) "_ _keyevent@0_ _:del" +3) "number" +1) "message"    //键message执行了DEL命令 +2) "_ _keyevent@0_ _:del" +3) "message" +``` +##第10章 RDB持久化 +RDB文件是保存了某个时间点数据库所有的键值对信息的压缩文件。 +RDB文件的创建 +可以使用SAVE命令创建RDB文件,在创建过程中,服务器进程会被阻塞,不能处理任何命令请求。 +也可以使用BGSAVE命令创建RDB文件,会派生出一个子进程,然后由子进程创建RDB文件,父进程继续处理命令,父进程在修改数据时,数据所在的内存页会被复制,父进程改的是复制后的数据,不影响子进程生成RDB文件。 +还原数据时的优先级 +因为AOF更新频率比RDB高,所以在还原数据时,优先使用AOF进行数据还原,然后再使用RDB文件进行数据还原。还原数据时,服务器也是属于阻塞状态,无法处理请求。 +生成RDB过程时的服务器状态 +在执行BGSAVE过程中,客户端发送的SAVE命令,BGSAVE命令会被拒绝,客户端发送的BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕后执行。 +BGREWRITEAOF执行过程中,客户端发送BGSAVE会被拒绝,BGREWRITEAOF和BGSAVE的工作都由子进程执行,操作中没有设密码冲突,主要是基于性能考虑。 +自动间歇性保存 +可以手动调用SAVE或BGSAVE命令生成RDB文件,也可以配置Redis服务器的save选项,让服务器在满足一定条件时自动执行BGSAVE命令,默认的save选项如下: +``` +save 900 1 +save 300 10 +save 60 10000 +``` +也就是当服务器满足900秒内执行了10次修改命令,或300秒内执行了10次修改命令,或60秒内执行了10000次修改命令时,服务器自动执行BGSAVE命令。 +``` +struct redisServer { +    // ... +    //  +记录了保存条件的数组 +    struct saveparam *saveparams; +    // ... +}; +``` +RedisServer使用saveparam数据结构来保存这些触发条件, +``` +struct saveparam { +    // 秒数 +    time_t seconds; +    // 修改数 +    int changes; +}; +``` +如果save选项的值为以下条件时,那么服务器状态中的saveparams数组将会是下图的样子。 +``` +save 900 1 +save 300 10 +save 60 10000” +``` +![image.png](../static/12609483-d3f2cd0e07a28217.png) +#####dirty计数器和lastsave属性 +Redis执行了修改命令后,会对dirty计数器+1,lastsave属性记录了上次成功执行SAVE和BGSAVE命令的时间。 +Redis的服务器周期性操作函数serverCron默认每隔100毫秒就会执行一次,该函数用于对正在运行的服务器进行维护,它的其中一项工作就是根据检查dirty计数器和lastsave属性来判断save选项所设置的保存条件是否已经满足,如果满足的话,就执行BGSAVE命令。 +#####RDB文件结构 +一个RDB文件由Redis字符串,db_version,database,EOF,check_sum五部分组成。如下图所示: +![image.png](../static/12609483-e3090ff08c7c67c5.png) +REDIS部分 +RDB最开始的是REDIS部分,长度是5字节,保存了'REDIS'五个字符,用于载入文件时快速判断是否是RDB文件。 +db_version部分 +db_version是四个字节,存储的是RDB文件的版本号 +databases部分 +databases部分包含了零个或多个数据库,以及各个数据库里的键值对数据。如果所有数据为空,那么这部分长度为0字节。 +EOF部分 +EOF常量长度为1字节,标志这个RDB文件正文结束。 +check_sum部分 +check_sum是一个8字节长的无符号整数,保存着一个校验和,这个校验和是程序通过对REDIS、db_version、databases、EOF四个部分的内容进行计算得出的。服务器在载入RDB文件时,会将载入数据所计算出的校验和与check_sum所记录的校验和进行对比,以此来检查RDB文件是否有出错或者损坏的情况出现。 + +#####database部分 +database部分会包含一个或多个数据库,如下图所示: +![image.png](../static/12609483-b587ac0f04f12a51.png) +每个数据库由SELECTDB,db_number、key_value_pairs三个部分组成, +![image.png](../static/12609483-c3480da40113b984.png) +SELECTDB常量是一个字节,用于标识,接下来的内容是数据库号码。 +db_number是一个数据库号码,可以是1字节,2字节或5字节。 +key_value_pairs保存了数据库的所有键值对,键值对分为不包含过期时间的键值对和包含过期时间的键值对,如下图所示, + +#####不包含过期时间的键值对 +主要由type,key,value组成 +![image.png](../static/12609483-9daf19837b2b6b8d.png) +type取值范围由以下几种,在还原数据时,读取键值对时,程序会根据type的取值来解析后面value。 + +``` +·REDIS_RDB_TYPE_STRING +·REDIS_RDB_TYPE_LIST +·REDIS_RDB_TYPE_SET +·REDIS_RDB_TYPE_ZSET +·REDIS_RDB_TYPE_HASH +·REDIS_RDB_TYPE_LIST_ZIPLIST +·REDIS_RDB_TYPE_SET_INTSET +·REDIS_RDB_TYPE_ZSET_ZIPLIST +·REDIS_RDB_TYPE_HASH_ZIPLIST +``` +#####包含过期时间的键值对 +由EXPIRETIME_MS,ms,TYPE,key,value组成 +EXPIRETIME_MS是1字节,用于标识,告知程序接下来的内容是一个以毫秒为单位的过期时间,ms是过期时间。 +![image.png](../static/12609483-97f154661ed5ecd0.png) +![image.png](../static/12609483-08f52cca19a78a04.png) + +#####value的编码 +value部分保存的是值对象,根据类型的不同,value部分的结构也不太一样。 +#####字符串对象 +字符串对象保存的是整数值时, +value结构如下: +![image.png](../static/12609483-5fdce25bd4b132d4.png) +![image.png](../static/12609483-0734e513fe56a194.png) +ENCODING可以是REDIS_RDB_ENC_INT8、REDIS_RDB_ENC_INT16或者REDIS_RDB_ENC_INT32三个常量的其中一个,它们分别代表RDB文件使用8位(bit)、16位或者32位来保存整数值integer +字符串对象保存的是字符串时,字节小于20字节时,会原样保存,否则会进行压缩,结构如下图所示: +![image.png](../static/12609483-19da8a08046df094.png) +![image.png](../static/12609483-84857b07e81cc097.png) +![image.png](../static/12609483-9f59a2ed26c36799.png) +“REDIS_RDB_ENC_LZF常量标志着字符串已经被LZF算法压缩过了,读入程序在碰到这个常量时,会根据之后的compressed_len压缩字符串长度、origin_len原字符串长度和compressed_string压缩字符串三部分,对字符串进行解压缩。 + +#####列表对象 + +如果TYPE的值为REDIS_RDB_TYPE_LIST,那么value保存的就是一个REDIS_ENCODING_LINKEDLIST编码的列表对象,结构如下图所示,list_length记录了列表的长度,它记录列表保存了多少个项(item),读入程序可以通过这个长度知道自己应该读入多少个列表项。 +图中以item开头的部分代表列表的项,因为每个列表项都是一个字符串对象,所以程序会以处理字符串对象的方式来保存和读入列表项。示例中第一个数字3是列表的长度,之后跟着的分别是第一个列表项、第二个列表项和第三个列表项,其中: +·第一个列表项的长度为5,内容为字符串"hello"。 +·第二个列表项的长度也为5,内容为字符串"world"。 +·第三个列表项的长度为1,内容为字符串"!"。 +![image.png](../static/12609483-e996e9bd2572dc30-9624620.png) +![image.png](../static/12609483-dcb976d2039fbc30.png) + +#####集合对象 +如果TYPE的值为REDIS_RDB_TYPE_SET,那么value保存的就是一个REDIS_ENCODING_HT编码的集合对象,结构跟列表结构类似,也是集合大小,然后后面是集合元素。如下图所示。结构中的第一个数字4记录了集合的大小,之后跟着的是集合的四个元素: +·第一个元素的长度为5,值为"apple"。 +·第二个元素的长度为6,值为"banana"。 +·第三个元素的长度为3,值为"cat"。 +·第四个元素的长度为3,值为"dog"。 +![image.png](../static/12609483-af0238d029c8e4e7.png) +![image.png](../static/12609483-e174c1c5d4283c0d.png) + +#####哈希表对象 +如果TYPE的值为REDIS_RDB_TYPE_HASH,那么value保存的就是一个REDIS_ENCODING_HT编码的集合对象,由hash_size和key_value_pair组成。 +·hash_size记录了哈希表的大小,也即是这个哈希表保存了多少键值对,读入程序可以通过这个大小知道自己应该读入多少个键值对。 +·以key_value_pair开头的部分代表哈希表中的键值对,键值对的键和值都是字符串对象,所以程序会以处理字符串对象的方式来保存和读入键值对。 +![image.png](../static/12609483-3232802e8c4f14a5-9624639.png) +![image.png](../static/12609483-5a15791898c0a7cf.png) + +#####有序集合对象 +如果TYPE的值为REDIS_RDB_TYPE_ZSET,那么value保存的就是一个REDIS_ENCODING_SKIPLIST编码的有序集合对象,结构如下图所示, +![image.png](../static/12609483-00ab7a17aa0f4d61.png) +在下图中,第一个数字2记录了有序集合的元素数量,之后跟着的是两个有序集合元素: +·第一个元素的成员是长度为2的字符串"pi",分值被转换成字符串之后变成了长度为4的字符串"3.14"。 +·第二个元素的成员是长度为1的字符串"e",分值被转换成字符串之后变成了长度为3的字符串"2.7"。” +![image.png](../static/12609483-a5f0454ae3254fb5.png) + +#####INTSET编码的集合 +如果TYPE的值为REDIS_RDB_TYPE_SET_INTSET,那么value保存的就是一个整数集合对象,RDB文件保存这种对象的方法是,先将整数集合转换为字符串对象,然后将这个字符串对象保存到RDB文件里面。 +如果程序在读入RDB文件的过程中,碰到由整数集合对象转换成的字符串对象,那么程序会根据TYPE值的指示,先读入字符串对象,再将这个字符串对象转换成原来的整数集合对象。 +#####ZIPLIST编码的列表、哈希表或者有序集合 +如果TYPE的值为REDIS_RDB_TYPE_LIST_ZIPLIST、REDIS_RDB_TYPE_HASH_ZIPLIST或者REDIS_RDB_TYPE_ZSET_ZIPLIST,那么value保存的就是一个压缩列表对象,RDB文件保存这种对象的方法是: +1)将压缩列表转换成一个字符串对象。 +2)将转换所得的字符串对象保存到RDB文件。 +如果程序在读入RDB文件的过程中,碰到由压缩列表对象转换成的字符串对象,那么程序会根据TYPE值的指示,执行以下操作: +读入字符串对象,并将它转换成type对应的压缩列表对象。 +###AOF持久化 +Redis服务器除了可以通过生成RDB文件来实现数据库持久化以外,还可以通过保存所有修改数据库的写命令请求来记录服务器的数据库状态,这就是AOF持久化。 +#####命令追加 +``` +struct redisServer { + sds aof_buf; +} +``` +redis服务器执行一个写命令以后,会以Redis文本协议的方式将写命令写入字符串对象aof_buf的末尾,例如 +那么服务器在执行这个SET命令之后, +``` +redis> SET KEY VALUE +OK +``` +会将以下协议内容追加到aof_buf缓冲区的末尾: +*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n +#####AOF文件写入和同步 +Redis服务器进程是一个事件循环,会依次接收客户端的命令请求,向客户端发送命令回复,及执行定时任务。循环结束之前会调用flushAppendOnlyFile函数,判断是否需要将aof_buf缓冲区的内容写入和保存到AOF文件中去。 +``` +“def eventLoop(): +    while True: +        # 处理文件事件,接收命令请求以及发送命令回复 +        # 处理命令请求时可能会有新内容被追加到 aof_buf 缓冲区中 +        processFileEvents() +        # 处理时间事件 +        processTimeEvents() +        # 考虑是否要将 aof_buf 中的内容写入和保存到 AOF 文件里面 +        flushAppendOnlyFile() +``` +在现代操作系统中,为了提高文件写入效率,调用write函数时,并不立即将数据写入磁盘,而是将写入数据保存在一个内存缓冲区,当缓冲区满了或超过指定的时间,才真正将缓冲区的数据写入到磁盘。 +所以flushAppendOnlyFile会根据服务器配置的appendfsync选项的值来决定同步的时机(也就是将数据真正写入磁盘中AOF文件的时机) +``` +appendfsync的三种选项: +always:服务器在每个事件循环都将aof_buf中所有内容写入AOF文件,并且同步AOF文件。(意味着出现故障停机,最多损失数据也就是一个事件循环的数据) +everysec:服务器在每个事件循环都将aof_buf中所有内容写入AOF文件,子线程每隔1s同步AOF文件。(意味着出现故障停机,最多损失数据也就是1s的数据) +no:服务器在每个事件循环都将aof_buf中所有内容写入AOF文件,由操作系统决定同步时间。 +``` +#####AOF文件载入和还原 +因为AOF文件里面包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍AOF文件里面保存的写命令,就可以还原之前的数据库状态。 +#####AOF文件重写 +随着服务器的运行,执行过的写命令越来越多,AOF文件也会越来越大。Redis提供了AOF文件重写功能,通过对读取当前的服务器状态生成需要的写命令,写入到一个新的AOF文件,然后替换旧的AOF文件。 +注意事项: +1.在目前版本中,REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值为64,这也就是说,一个写命令最多包含64个元素。如果一个集合键包含了超过64个元素,那么重写程序会用多条SADD命令来记录这个集合。 +#####AOF后台重写 +AOF重写命令aof_rewrite函数可以很好地完成创建一个新AOF文件的任务,但是,因为这个函数会进行大量的写入操作,所以调用这个函数的线程将被长时间阻塞,因为Redis服务器使用单个线程来处理命令请求,所以如果由服务器直接调用aof_rewrite函数的话,那么在重写AOF文件期间,服务期将无法处理客户端发来的命令请求。所以可以使用BGREWRITEAOF命令在后台进行aof文件重写。Redis会fork一个子进程对aof文件进行重写,父进程可以继续处理客户端的请求,当父进程执行写命令时,会对数据所在的内存页进行拷贝,修改的就是内存页的副本,不影响子进程的写入。在写入期间,当Redis服务器接收到写命令后,会进行如下操作: +1)执行客户端发来的命令。 +2)将执行后的写命令追加到AOF缓冲区。(以防aof重写失败后,不影响旧的aof文件)。 +3)将执行后的写命令追加到AOF重写缓冲区。(便于aof重写成功后,将重写期间执行的写命令添加到新的aof文件)。 +当子进程完成AOF重写工作之后,它会向父进程发送一个信号,父进程在接到该信号之后,会调用一个信号处理函数,并执行以下工作: +1)将AOF重写缓冲区中的所有内容写入到新AOF文件中,这时新AOF文件所保存的数据库状态将和服务器当前的数据库状态一致。 +2)对新的AOF文件进行改名,原子地(atomic)覆盖现有的AOF文件,完成新旧两个AOF文件的替换。 +这个信号处理函数执行完毕之后,父进程就可以继续像往常一样接受命令请求了。 +### 第13章 客户端 +Redis服务器保存了一个clients属性,是一个链表,保存了所有与服务器连接的客户端的状态信息,对客户端批量操作,查找某个指定客户端,都可以通过遍历clients链表完成。 +``` +struct redisServer { + list *client; +} +``` +#####客户端属性 +客户端状态包含的属性分为通用属性和与执行特定功能相关的属性。(比如操作数据库时需要用到的db属性和dictid属性,执行事务时需要用到的mstate属性,以及执行WATCH命令时需要用到的watched_keys属性等等) +``` +typedef struct redisClient { + int fd;//Socket描述符 + robj *name;//名称,不设置名字时,默认为NULL + int flags;//标志,用于标识客户端的状态 + sds querybuf;//输入缓冲区 + robj **argv;//保存要执行的命令及传给命令的参数 + int argc;//记录argv数组的长度 + struct redisCommand *cmd;//将要执行的命令的实现 +} +``` +Socket描述符 +fd是Socket描述符,会记录客户端正在使用的Socket描述符,一般是一个大于-1的整数,当为-1时,代表当前客户端是伪客户端,不需要Socket连接,(“会在两个地方用到伪客户端,一个用于载入AOF文件并还原数据库状态,而另一个则用于执行Lua脚本中包含的Redis命令) +以下是一些flags属性的例子: +``` +# 客户端是一个主服务器 +REDIS_MASTER +# 客户端正在被列表命令阻塞 +REDIS_BLOCKED +# 客户端正在执行事务,但事务的安全性已被破坏 +REDIS_MULTI | REDIS_DIRTY_CAS +# 客户端是一个从服务器,并且版本低于Redis 2.8  +REDIS_SLAVE | REDIS_PRE_PSYNC +# 这是专门用于执行Lua脚本包含的Redis命令的伪客户端 +# 它强制服务器将当前执行的命令写入AOF文件,并复制给从服务器 +REDIS_LUA_CLIENT | REDIS_FORCE_AOF| REDIS_FORCE_REPL +``` +输入缓冲区 +是一个Redis字符串对象,保存了客户端向服务端发送的命令,输入缓冲区的大小会根据输入内容动态地缩小或者扩大,但它的最大大小不能超过1GB,否则服务器将关闭这个客户端。“如果客户端向服务器发送了以下命令请求: +SET key value +那么客户端状态的querybuf属性将是一个包含以下内容的SDS值: +*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n +命令与命令参数 +在服务器将客户端发送的命令请求保存到客户端状态的querybuf属性之后,服务器将对命令请求的内容进行分析,并将得出的命令参数以及命令参数的个数分别保存到客户端状态的argv属性和argc属性。如下图所示: +![image.png](../static/12609483-3850a513cef3ae99.png) +命令的实现函数 +当服务器从协议内容中分析并得出argv属性和argc属性的值之后,服务器将根据项argv[0]的值,在命令表中查找命令所对应的命令实现函数,找到后将redisClient中的cmd属性指向对应的命令实现函数。 +命令表是一个字典,字典的键是一个SDS结构,保存了命令的名字,字典的值是命令所对应的redisCommand结构,这个结构保存了命令的实现函数、命令的标志、命令应该给定的参数个数、命令的总执行次数和总消耗时长等统计信息。如下图所示: +![image.png](../static/12609483-d2dd19e0d29e9b1e-9624693.png) +![image.png](../static/12609483-354d0c595c824428.png) + +输出缓冲区 +每个客户端都有两个输出缓冲区可用,一个缓冲区的大小是固定的,另一个缓冲区的大小是可变的,用于保存执行命令后的回复。 +固定大小的缓冲区用于保存那些长度比较小的回复,比如OK、简短的字符串值、整数值、错误回复等等。 +可变大小的缓冲区用于保存那些长度比较大的回复,比如一个非常长的字符串值,一个由很多项组成的列表,一个包含了很多元素的集合等等。 +客户端的固定大小缓冲区由buf和bufpos两个属性组成: +typedef struct redisClient { +    char buf[REDIS_REPLY_CHUNK_BYTES]; +    int bufpos; +} redisClient; +buf是一个大小为REDIS_REPLY_CHUNK_BYTES字节的字节数组,而bufpos属性则记录了buf数组目前已使用的字节数量。 +REDIS_REPLY_CHUNK_BYTES常量目前的默认值为16*1024,也即是说,buf数组的默认大小为16KB。 +身份验证 +``` +typedef struct redisClient { +    int authenticated; +} +``` +redisClient的authenticated属性用于记录客户端是否通过了身份验证,authenticated的值为0,那么表示客户端未通过身份验证,只能服务器只会执行AUTH命令,其他命令都会被拒绝执行。 +时间 +``` +typedef struct redisClient { +    time_t ctime;//ctime属性记录了创建客户端链接的时间 +    time_t lastinteraction;//lastinteraction属性记录了客户端与服务器最后一次进行互动的时间 +    time_t obuf_soft_limit_reached_time;//记录了输出缓冲区第一次到达软性限制(soft limit)的时间 +} +``` +客户端的创建与关闭 +如果客户端是通过网络连接与服务器进行连接的普通客户端,那么在客户端使用connect函数连接到服务器时,服务器就会调用连接事件处理器(在第12章有介绍),为客户端创建相应的客户端状态,并将这个新的客户端状态添加到服务器状态结构clients链表的末尾。 +一个普通客户端可以因为多种原因而被关闭,客户端进程退出或被杀死等,也可以是因为回复过大,占用过多的服务器资源,导致输出缓冲区超出范围,被执行相应的限制操作。有两种模式可以限制客户端输出缓冲区的大小 +硬性限制,超出硬性限制所设置的大小时,立即关闭客户端。 +软性限制,超出软性限制所设置的大小,但没有超过超出硬性限制所设置的大小时,会使用服务器将使用客户端状态结构的obuf_soft_limit_reached_time属性记录下客户端到达软性限制的起始时间;之后服务器会继续监视客户端,如果输出缓冲区的大小一直超出软性限制,并且持续时间超过服务器设定的时长,那么服务器将关闭客户端;相反地,如果输出缓冲区的大小在指定时间内,不再超出软性限制,那么客户端就不会被关闭,并且obuf_soft_limit_reached_time属性的值也会被清零。 +伪客户端 +服务器会在初始化时创建负责执行Lua脚本中包含的Redis命令的伪客户端,并将这个伪客户端关联在服务器状态结构的lua_client属性中,lua_client伪客户端在服务器运行的整个生命期中会一直存在,只有服务器被关闭时,这个客户端才会被关闭。在载入AOF文件时,服务器会创建用于执行AOF文件包含的Redis命令的伪客户端,并在载入完成之后,关闭这个伪客户端。 +### 第14章 服务器 +这一章主要讲服务器处理命令请求的整个过程及对serverCron函数的介绍。 +##### 命令执行的过程 +(以SET KEY VALUE命令为例) +1.用户在客户端输入了"SET KEY VALUE"命令,客户端会将命令请求转换为文本协议格式 +*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n +然后通过连接到服务器的Socket,将文本协议格式的命令请求发送给服务器。 +2.服务器通过Socket接收到文本协议格式的命令请求后,将其保存到redisClient的输入缓冲区。 +3.对输入缓冲区中的命令请求进行分析,提取出相应的命令及命令请求的参数,将命令,命令参数保存到redisClient的argv数组中去,并且对argv数组的长度属性argc进行赋值。 +4.调用命令执行器执行命令,命令执行器首先根据argv[0]的值去命令表中查找相应命令,并且将找到的命令保存到redisClient中的cmd属性里面。(命令表是一个字典,key保存了命令的名字,value则是redisCommand结构,保存了命令相关的信息,下面的图展示了redisCommand的属性及区分命令类型的sflags属性) +5.服务器已经将执行命令所需的命令实现函数(保存在客户端状态的cmd属性)、参数(保存在客户端状态的argv属性)、参数个数(保存在客户端状态的argc属性)都收集齐了,但是在真正执行命令之前,程序还需要进行检查(例如检查cmd属性的值是否为NULL),保证命令可以正确执行。 +6.因为执行命令的实现保存在redisClient的cmd属性中,参数和参数个数保存在redisClient的argv,argc属性中,所以真正执行时,只要执行以下语句就行 +``` +client->cmd->proc(client); +``` +7.实现函数执行完毕后,服务器还需要执行一些后续工作: +·如果服务器开启了慢查询日志功能,那么慢查询日志模块会检查是否需要为刚刚执行完的命令请求添加一条新的慢查询日志。 +·根据刚刚执行命令所耗费的时长,更新被执行命令的redisCommand结构的milliseconds属性,并将命令的redisCommand结构的calls计数器的值增一。 +·如果服务器开启了AOF持久化功能,那么AOF持久化模块会将刚刚执行的命令请求写入到AOF缓冲区里面。 +·如果有其他从服务器正在复制当前这个服务器,那么服务器会将刚刚执行的命令传播给所有从服务器。 +当以上操作都执行完了之后,服务器对于当前命令的执行到此就告一段落了,之后服务器就可以继续从文件事件处理器中取出并处理下一个命令请求了。 +8.命令实现函数将命令回复保存到客户端的输出缓冲区里面,并为客户端的Socket关联命令回复处理器,当客户端套接字变为可写状态时,服务器就会执行命令回复处理器,将保存在客户端输出缓冲区中的命令回复发送给客户端。当命令回复发送完毕之后,回复处理器会清空redisClient的输出缓冲区,为处理下一个命令请求做好准备。 +9.当客户端接收到协议格式的命令回复之后,它会将这些回复转换成人类可读的格式,并打印给用户观看。 +![image.png](../static/12609483-b05ceaabc0e3b6a6.png) +![image.png](../static/12609483-b4d2e3ca90ae600b.png) + +###serverCron函数 +Redis服务器中的serverCron函数默认每隔100毫秒执行一次,这个函数负责管理服务器的资源,并保持服务器自身的良好运转,在serverCron函数中会对以下属性进行更新: +1.缓存的秒级精度系统时间和毫秒级精度系统时间(默认100毫秒更新一次) +2.缓存的lrulock属性,保存了服务器的LRU时钟,主要用于给对象计算键空转时间(空转时间=对象LRU时间-服务器的lrulock属性)。(默认10s更新一次) +3.更新服务器每秒执行命令次数instantaneous_ops_per_sec,是通过计算服务器每1毫秒内执行命令数*1000估算出来的。 +4.更新服务器内存峰值记录。 +5.在启动服务器时,Redis会为服务器进程的SIGTERM信号关联处理器sigtermHandler函数,这个信号处理器负责在服务器接到SIGTERM信号时,打开服务器状态的shutdown_asap标识,“每次serverCron函数运行时,程序都会对服务器状态的shutdown_asap属性进行检查,并根据属性的值决定是否关闭服务器。(关闭之前会进行RDB持久化) +6.管理客户端资源,serverCron函数每次执行都会调用clientsCron函数,clientsCron函数会对一定数量的客户端进行以下两个检查: +·如果客户端与服务器之间的连接已经超时(很长一段时间里客户端和服务器都没有互动),那么程序释放这个客户端。 +·如果客户端在上一次执行命令请求之后,输入缓冲区的大小超过了一定的长度,那么程序会释放客户端当前的输入缓冲区,并重新创建一个默认大小的输入缓冲区,从而防止客户端的输入缓冲区耗费了过多的内存。 +7.管理数据库资源。对数据库进行检查,删除过期键。 +8.在服务器执行BGSAVE命令的期间,如果客户端向服务器发来BGREWRITEAOF命令,那么服务器会将BGREWRITEAOF命令的执行时间延迟到BGSAVE命令执行完毕之后,serverCron函数会检查是否有被延迟执行的BGREWRITEAOF命令,如果有,并且当前没有BGSAVE和BGREWRITEAOF命令在执行,那么就会执行BGREWRITEAOF命令。 +9.持久化操作检查。serverCron函数会检查rdb_child_pid和aof_child_pid两个属性来判断当前是否在在进行持久化操作,在的话,执行wait3函数,检查子进程是否有信号发来服务器进程: +·如果有信号到达,那么表示新的RDB文件已经生成完毕(对于BGSAVE命令来说),或者AOF文件已经重写完毕(对于BGREWRITEAOF命令来说),服务器需要进行相应命令的后续操作,比如用新的RDB文件替换现有的RDB文件,或者用重写后的AOF文件替换现有的AOF文件。 +·如果没有信号到达,那么表示持久化操作未完成,程序不做动作。 +如果没有在进行持久化,那么会判断当前是否满足进行RDB持久话或者AOF持久化的条件,满足就执行相关操作。如下图所示: +![image.png](../static/12609483-b4263d32e9ae0d5f.png) +10.如果服务器开启了AOF持久化功能,并且AOF缓冲区里面还有待写入的数据,那么serverCron函数会调用相应的程序,将AOF缓冲区中的内容写入到AOF文件里面。 +11.关闭异步客户端,服务器会关闭那些输出缓冲区大小超出限制的客户端。 +12.增加cronloops计数器的值。服务器状态的cronloops属性记录了serverCron函数执行的次数,主要用于复制模块中实现“每执行serverCron函数N次就执行一次指定代码”的功能。 + +#####初始化服务器 +一个Redis服务器从启动到能够接受客户端的命令请求,需要经过一系列的初始化和设置过程。主要由以下步骤: +1.初始化服务器状态结构 +初始化服务器的第一步就是创建一个struct redisServer类型的实例变量server作为服务器的状态,并为结构中的各个属性设置默认值。 +2.载入配置选项 +在启动服务器时,用户可以通过给定配置参数或者指定配置文件来修改服务器的默认配置 +3.初始化服务器数据结构 +在之前执行initServerConfig函数初始化server状态时,程序只创建了命令表一个数据结构,不过除了命令表之外,服务器状态还包含其他数据结构。 +4.还原数据库状态 +在完成了对服务器状态server变量的初始化之后,服务器需要载入RDB文件或者AOF文件,并根据文件记录的内容来还原服务器的数据库状态。 +5.以上步骤执行完后,开始执行事件循环。 + diff --git a/docs/RedisBook2.md b/docs/RedisBook2.md new file mode 100644 index 0000000..0339804 --- /dev/null +++ b/docs/RedisBook2.md @@ -0,0 +1,591 @@ +##客官,这是一份精心编写的《Redis设计与实现》读书心得(上篇) + +### 第15章 复制 + +在Redis中,可以通过执行SLAVEOF命令或者设置slaveof选项,让从服务器来备份主服务器上的数据。 +Redis的复制功能主要分为同步和命令传播。 +同步主要是指从服务器的状态更新为主服务器的状态。同步具体又细分为完整重同步和部分重同步(Redis 2.8以后的版本才有)。 +#####完整重同步 +一般是从服务器向主服务器发送命令请求,主服务向从服务器发送RDB文件及缓冲区保存的写命令,从服务器根据RDB文件和缓冲区保存的写命令恢复数据库状态。 +具体步骤如下: +1)从服务器向主服务器发送SYNC命令。 +2)收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。 +3)当主服务器的BGSAVE命令执行完毕时,主服务器会将BGSAVE命令生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态。 +4)主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。 +如下图所示: +![image.png](../static/12609483-d09569b3e780120b.png) + +#####命令传播 +在同步操作执行完毕之后的这个时间点,主从服务器两者的数据库将达到一致状态。但之后当主服务器执行客户端发送的写命令时,主服务器的数据库就有可能会被修改,并导致主从服务器状态不再一致,所以命令传播就是主服务器执行一条写命令后,也会把这条写命令发送给从服务器执行。 +#####部分重同步(Redis2.8以后版本才有) +对于从服务器初次复制的场景来说,完整重同步+命令传播已经可以完美得满足需求了,但是如果从服务器是断线后重复制,因为从服务器本身拥有主服务器的数据,只是缺少断线期间的数据修改,采用完整重同步+命令传播会效率比较低。所以就有了部分重同步只会向从服务器发送断线期间的写命令。 +部分重同步的实现由三部分组成,复制偏移量,复制积压缓冲区,服务器运行ID。 +复制偏移量 +指的是执行复制的双方——主服务器和从服务器会分别维护一个复制偏移量: +·主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N。 +·从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量的值加上N。 +这样通过对比主从服务器的复制偏移量可以知道主从服务器目前的数据状态。 +复制积压缓冲区 +复制积压缓冲区是由主服务器维护的一个固定长度先进先出队列,保存了主服务器执行的写命令。默认大小为1MB。队列长度是固定的,当元素满了时,会将最先入队的元素弹出,再将新元素放入队列。 +服务器运行ID +每个Redis服务器,不论主服务器还是从服务,都会有自己的运行ID。当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来。 +当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的运行ID: +·如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,那么说明从服务器断线之前复制的就是当前连接的这个主服务器,主服务器可以继续尝试执行部分重同步操作。 +·相反地,如果从服务器保存的运行ID和当前连接的主服务器的运行ID并不相同,那么说明从服务器断线之前复制的主服务“·相反地,如果从服务器保存的运行ID和当前连接的主服务器的运行ID并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器将对从服务器执行完整重同步操作。 +##### PSYNC命令的实现 +PSYNC有两种模式,可以进行完整重同步和部分重同步。从服务器在开始一次新的复制时将向主服务器发送PSYNC ? -1命令,主动请求主服务器进行完整重同步。如果从服务器已经复制过某个主服务器,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC 命令:其中runid是上一次复制的主服务器的运行ID,而offset则是从服务器当前的复制偏移量,接收到这个命令的主服务器会通过这两个参数来判断应该对从服务器执行哪种同步操作。具体流程图如下: +![image.png](../static/12609483-5d5aecc951557f56.png) + +##### 复制的实现 +向从服务发送SLAVEOF命令,可以让一个从服务器复制主服务。 +例如:向从服务器发送下面的命令,复制地址为127.0.0.1,端口为6379的主服务器的数据。 +``` +SLAVEOF 127.0.0.1 6379 +``` +实现步骤如下: +1.在从服务redisServer的设置主服务器的地址masterhost和端口masterport +``` +struct redisServer { + // 主服务器的地址 + char *masterhost; + // 主服务器的端口 + int masterport; +}; +``` +2.建立Socket连接 +3.发送PING命令 +4.身份验证 +5.发送端口信息 +6.同步 +7.命令传播,心跳检测,检测主从服务器的网络连接状态,“辅助实现min-slaves配置选项,检测命令丢失。 +### 第16章 哨兵 +哨兵系统指的是由一个或多个哨兵实例组成的哨兵系统可以监视任意多个主服务器及属下的所有从服务器,在被监视的主服务器进入下线状态时,自动将某个从服务器升级为主服务器,并且由新的主服务代替旧的主服务器继续处理命令请求。 +#####启动哨兵服务器 +哨兵服务器本质上搜索一个运行在特殊模式下的Redis服务器,哨兵服务器启动的实现主要分为三步: +1.初始化服务器。与普通Redis服务器不同的是,初始化时不需要通过载入RDB文件或者AOF文件还原数据库状态。下图为哨兵服务器的主要功能: +![image.png](../static/12609483-1829498245a5787b.png) +2.使用哨兵专用代码。也就是将一部分普通Redis服务器使用的代码替换为哨兵服务器使用的代码。例如:普通Redis服务器使用redis.c/redisCommandTable作为服务器的命令表,因为SET,SBSIZE等很多命令哨兵服务器不会执行,所以哨兵服务器使用sentinel.c/sentinelcmds作为服务器的命令表,并且其中的INFO命令会使用Sentinel模式下的专用实现sentinel.c/sentinelInfoCommand函数,而不是普通Redis服务器使用的实现redis.c/infoCommand函数。 +3.初始化哨兵服务器的状态。普通服务器的一般状态仍然由redis.h/redisServer结构保存,哨兵服务器会初始化一个sentinel.c/sentinelState结构,这个结构保存了服务器中所有和Sentinel功能有关的状态。 + +``` +struct sentinelState { + // 当前纪元,用于实现故障转移 + uint64_t current_epoch; + // 保存了所有被这个哨兵服务器监视的主服务器 + // 字典的键是主服务器的名字 + // 字典的值则是一个指向sentinelRedisInstance结构的指针 + dict *masters; + // 是否进入了TILT模式? + int tilt; + // 目前正在执行的脚本的数量 + int running_scripts; + // 进入TILT模式的时间 + mstime_t tilt_start_time; + // 最后一次执行时间处理器的时间 + mstime_t previous_time; + // 一个FIFO队列,包含了所有需要执行的用户脚本 + list *scripts_queue; +} sentinel; +``` +保存了被监视的主服务器信息的sentinelRedisInstance结构 +``` +typedef struct sentinelRedisInstance { + // 标识值,记录了实例的类型,以及该实例的当前状态 + int flags; + // 实例的名字 + // 主服务器的名字由用户在配置文件中设置 + // 从服务器以及Sentinel的名字由Sentinel自动设置 + // 格式为ip:port,例如"127.0.0.1:26379" + char *name; + // 实例的运行ID + char *runid; + // 配置纪元,用于实现故障转移 + uint64_t config_epoch; + // 实例的地址 + sentinelAddr *addr; + // SENTINEL down-after-milliseconds选项设定的值 + // 实例无响应多少毫秒之后才会被判断为主观下线(subjectively down) + mstime_t down_after_period; + // SENTINEL monitor +选项中的quorum参数 + // 判断这个实例为客观下线(objectively down +)所需的支持投票数量 + int quorum; + // SENTINEL parallel-syncs +选项的值 + // +在执行故障转移操作时,可以同时对新的主服务器进行同步的从服务器数量 + int parallel_syncs; + // SENTINEL failover-timeout +选项的值 + // 刷新故障迁移状态的最大时限 + mstime_t failover_timeout; + // ... +} sentinelRedisInstance; +``` +4.创建连向主服务器的网络连接。哨兵服务器会创建两个连向被监视的主服务器的异步网络连接,一个命令连接,用于向主服务器发送命令,并接受命令回复。另一个是订阅连接,用于订阅主服务器的__sentinel__:hello频道。通过这个频道,哨兵服务器可以通过主服务器了解其他哨兵服务器,从服务器等信息。 +##### 命令连接-获取主服务器信息 +哨兵服务器会以每十秒一次的频率,通过命令链接向被监视的主服务器发送INFO命令,并根据回复来获取主服务器的当前信息。 +能获取到的信息如下: +1.主服务器的相关信息(运行ID,角色等) +2.主服务器下属的所有从服务器的信息(服务器的角色,IP,端口,在线状态等) +根据这些信息,哨兵对象可以更新自身的sentinelRedisInstance结构中的主服务器和从服务器信息。(可以自动发现从服务器的信息) +![image.png](../static/12609483-a1800dd48244e22d.png) + +##### 命令连接-获取从服务器的信息 +当哨兵对象发现新的从服务器出现时, 会为它创建实例结构,而且会创建连接到从服务器的命令连接和订阅连接。并且会以每十秒一次的频率通过命令连接向从服务器发送INFO命令,获取从服务器及它所属的主服务器的信息。主要获得的信息如下: +从服务器的运行ID,角色role。 +主服务器的IP地址,端口号master_port,主从服务器的连接状态,从服务器的优先级slave_priority,从服务器的复制偏移量slave_repl_offset。 +下图展示了根据上面的INFO命令回复对从服务器的实例结构进行更新之后的情况: +![image.png](../static/12609483-d84b28aaf117b063.png) + +##### 向主服务器和从服务器发送信息 +在默认情况下,Sentinel会以每两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送PUBLISH的命令,格式如下: +``` +PUBLISH __sentinel__:hello ",,,,,,, +``` +参数包括哨兵服务器的IP,端口,运行ID,配置纪元。主服务器的名字,IP,端口,配置纪元。 +以下是一条Sentinel通过PUBLISH命令向主服务器发送的信息示例: +``` +"127.0.0.1,26379,e955b4c85598ef5b5f055bc7ebfd5e828dbed4fa,0,mymaster,127.0.0.1,6379,0 +``` +这个示例包含了以下信息: +·Sentinel的IP地址为127.0.0.1端口号为26379,运行ID为e955b4c85598ef5b5f055bc7ebfd5e828dbed4fa,当前的配置纪元为0。 +·主服务器的名字为mymaster,IP地址为127.0.0.1,端口号为6379,当前的配置纪元为0。 +##### 接收来自主服务器和从服务器的频道信息 +当Sentinel与一个主服务器或者从服务器建立起订阅连接之后,Sentinel就会通过订阅连接,向服务器发送以下命令: +``` +SUBSCRIBE __sentinel__:hello +``` +Sentinel对__sentinel__:hello频道的订阅会一直持续到Sentinel与服务器的连接断开为止。对于每个与Sentinel连接的服务器,Sentinel既通过命令连接向服务器的__sentinel__:hello频道发送信息,又通过订阅连接从服务器的__sentinel__:hello频道接收信息。当sentinel1向服务器的__sentinel__:hello频道发送一条信息时,所有订阅了__sentinel__:hello频道的Sentinel(包括sentinel1自己在内)都会收到这条信息,然后对相应主服务器的实例结构进行更新。 +##### 更新sentinels字典 +Sentinel为主服务器创建的实例结构中的sentinels字典保存了除Sentinel本身之外,还保存了所有同样监视这个主服务器的其他Sentinel的信息。当一个Sentinel收到其他Sentinel发来的信息时,会对消息解析,更新sentinels字典。 + +·sentinels字典的键是其中一个Sentinel的名字,格式为ip:port,比如对于IP地址为127.0.0.1,端口号为26379的Sentinel来说,这个Sentinel在sentinels字典中的键就是"127.0.0.1:26379"。 +·sentinels字典的值则是键所对应Sentinel的实例结构,比如对于键"127.0.0.1:26379"来说,这个键在sentinels字典中的值就是IP为127.0.0.1,端口号为26379的Sentinel的实例结构。 +#####“创建连向其他Sentinel的命令连接 +当Sentinel通过频道信息发现一个新的Sentinel时,它不仅会为新Sentinel在sentinels字典中创建相应的实例结构,还会创建一个连向新Sentinel的命令连接,而新Sentinel也同样会创建连向这个Sentinel的命令连接,最终监视同一主服务器的多个Sentinel将形成相互连接的网络:Sentinel A有连向Sentinel B的命令连接,而Sentinel B也有连向Sentinel A的命令连接,如下图所示: +![image.png](../static/12609483-51f11addf81dd7bf.png) +Sentinel之间不会创建订阅连接 +Sentinel在连接主服务器或者从服务器时,会同时创建命令连接和订阅连接,但是在连接其他Sentinel时,却只会创建命令连接,而不创建订阅连接。因为Sentinel需要通过接收主服务器或者从服务器发来的频道信息来发现未知的新Sentinel,所以才需要建立订阅连接,而相互已知的Sentinel只要使用命令连接来进行通信就足够了。 + +##### 检测主观下线状态 +在默认情况下,Sentinel会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器、从服务器、其他Sentinel在内)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线。 +·有效回复:实例返回+PONG、-LOADING、-MASTERDOWN三种回复的其中一种。 +·无效回复:实例返回除+PONG、-LOADING、-MASTERDOWN三种回复之外的其他回复,或者在指定时限内没有返回任何回复。 +Sentinel配置文件中的down-after-milliseconds选项指定了Sentinel判断实例进入主观下线所需的时间长度:如果一个实例在down-after-milliseconds毫秒内,连续向Sentinel返回无效回复,那么Sentinel会修改这个实例所对应的实例结构,在结构的flags属性中打开SRI_S_DOWN标识,以此来表示这个实例已经进入主观下线状态。 +##### 检测客观下线状态 +当Sentinel将一个主服务器判断为主观下线之后,为了确认这个主服务器是否真的下线了,它会向同样监视这一主服务器的其他Sentinel发送"SENTINEL is-master-down-by-addr”命令,看它们是否也认为主服务器已经进入了下线状态(可以是主观下线或者客观下线)。当Sentinel从其他Sentinel那里接收到足够数量的已下线判断之后,Sentinel就会将从服务器判定为客观下线,并对主服务器执行故障转移操作。 +客观下线状态的判断条件 +Sentinel配置中有一个quorum属性,当有quorum个数的Sentinel认为主服务进入下线状态时,Sentinel便将主服务器判定位客户下线。(每个Sentinel的quorum可以不同)。 +##### 选举领头Sentinel +当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线主服务器执行故障转移操作。 +以下是Redis选举领头Sentinel的规则和方法: +1.每个Sentinel(源Sentinel)向另一个Sentinel(目标Sentinel)发送SENTINEL is-master-down-by-addr命令,要求后者将前者设置为局部领头Sentinel,每个Sentinel设置局部领头Sentinel规则都是先到先得,最先向目标Sentinel发送设置要求的源Sentinel将成为目标Sentinel的局部领头Sentinel,而之后接收到的所有设置要求都会被目标Sentinel拒绝。 +2.局部领头一旦设置,目标Sentinel会将配置纪元+1,并且给源Sentinel回复局部领头Sentinel的运行ID和配置纪元。 +3.源Sentinel在接收到目标Sentinel返回的命令回复之后,会检查回复中leader_epoch参数的值和自己的配置纪元是否相同,如果相同的话,那么源Sentinel继续取出回复中的leader_runid参数,如果leader_runid参数的值和源Sentinel的运行ID一致,那么表示目标Sentinel将源Sentinel设置成了局部领头Sentinel。 +4.如果有某个Sentinel被半数以上的Sentinel设置成了局部领头Sentinel,那么这个Sentinel成为领头Sentinel。 +5.如果在给定时限内,没有一个Sentinel被选举为领头Sentinel,那么各个Sentinel将在一段时间之后再次进行选举,直到选出领头Sentinel为止。 +##### 故障转移 +在选举产生出领头Sentinel之后,领头Sentinel将对已下线的主服务器执行故障转移操作,该操作包含以下三个步骤: +1.在已下线主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器。 +挑选规则: +1)删除列表中所有处于下线或者断线状态的从服务器 +2)删除列表中所有最近五秒内没有回复过领头Sentinel的INFO命令的从服务器 +3)删除所有与已下线主服务器连接断开超过down-after-milliseconds*10毫秒的从服务器:down-after-milliseconds选项指定了判断主服务器下线所需的时间,而删除断开时长超过down-after-milliseconds*10毫秒的从服务器,则可以保证列表中剩余的从服务器都没有过早地与主服务器断开连接,换句话说,列表中剩余的从服务器保存的数据都是比较新的。 +之后,领头Sentinel将根据从服务器的优先级,对列表中剩余的从服务器进行排序,并选出其中优先级最高的从服务器。 +2.领头Sentinel向从服务器发送SLAVEOF命令,让已下线主服务器属下的所有从服务器改为复制新的主服务器。 +3.当这个旧的主服务器重新上线时,领头Sentinel向它发送SLAVEOF命令将已下线主服务器设置为新的主服务器的从服务器。 +### 第17章 集群 +Redis集群是一个分布式数据库方案,可以通过分片来进行数据共享,并提供复制和故障转移功能。 +##### 节点 +节点只是一个运行在集群模式下的Redis服务器,由启动时配置中的cluster-enabled属性决定。 +##### 集群数据结构 +使用clusterNode结构可以保存了一个节点的当前状态,例如创建时间,名字,配置纪元,IP,端口等。 +![。](../static/12609483-6264bb0a50df8696.png) +clusterNode结构的link属性是一个clusterLink结构,该结构保存了连接节点所需的有关信息,比如套接字描述符,输入缓冲区和输出缓冲区等。(有点类似于redisClient结构中用于连接客户端的套接字,缓冲区) +![image.png](../static/12609483-6f7331032865b57b.png) +每个节点都保存着一个clusterState结构,这个结构记录了在当前节点的视角下,集群目前所处的状态,例如集群是在线还是下线,集群包含多少个节点,集群当前的配置纪元等。 +![image.png](../static/12609483-fbee5c2cfb8601da.png) + +#####槽指派 +Redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为16384个槽(slot),分配给每个节点处理,数据库中的每个键都属于这16384个槽的其中一个。可以使用CLUSTER MEET命令对槽进行分配。 +clusterNode结构中的slots属性和numslot属性记录了节点处理哪些槽,numslot属性记录了当前节点处理的槽的数量,slots属性是一个二进制位数组,一共有16384位,数组第i位上的值代表节点是否处理槽i。 +![image.png](../static/12609483-b20cd2594752fad8.png) +下图展示了一个slots数组示例:这个数组索引0至索引7上的二进制位的值都为1,其余所有二进制位的值都为0,这表示节点负责处理槽0至槽7。 +![image.png](../static/12609483-b201b1635c4b2125.png) + +##### 记录集群所有槽的指派信息 +除了记录自身节点负责的槽位信息以外,clusterState结构中的slots数组记录了集群中所有16384个槽的指派信息。 +![image.png](../static/12609483-f4dc3c2e06dc055e.png) +slots数组包含16384个项,每个数组项都是一个指向对应的槽所在的clusterNode结构的指针,如果slots[i]指针指向NULL,那么表示槽i尚未指派给任何节点。 +clusterNode.slots 与 clusterState.slots +如果只有clusterNode.slots ,想要知道某个槽被指派给哪个节点,需要以O(N)的复杂度对clusterState.Nodes字典中每个节点的slots数组进行遍历,而通过clusterState.slots查找只需要O(1)复杂度。 +如果只有clusterState.slots ,想要将某个节点的槽指派信息发送给其他节点,需要以O(N)的复杂度对clusterState.slots数组进行遍历,而通过clusterState.slots可以直接发送。 + +#####在集群中执行命令 +在对数据库中的16384个槽都进行了指派之后,集群就会进入上线状态,这时客户端就可以向集群中的节点发送数据命令了。 +当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽所在的节点,如果键所在的槽正好就指派给了当前节点,那么节点直接执行这个命令。如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误,指引客户端转向(redirect)至正确的节点,并客户端会向正确的节点再次发送之前想要执行的命令,并且之后这个槽的对应的键也会直接往正确的节点发送。 +#####“计算键属于哪个槽 +节点使用以下算法来计算给定键key属于哪个槽 +``` +def slot_number(key): + return CRC16(key) & 16383 +``` +其中CRC16(key)语句用于计算键key的CRC-16校验和,而&16383语句则用于计算出一个介于0至16383之间的整数作为键key的槽号。 +#####“节点数据库的实现 +集群节点保存键值对以及键值对过期时间的方式,与的单机Redis服务器保存键值对以及键值对过期时间的方式完全相同。节点和单机服务器在数据库方面的一个区别是,节点只能使用0号数据库。 +下图展示了节点7000的数据库状态,数据库中包含列表键"lst",哈希键"book",以及字符串键"date",其中键"lst"和键"book"带有过期时间。 +![image.png](../static/12609483-49b9a8d5e04cce51.png) +除了将键值对保存在数据库里面之外,节点还会用clusterState结构中的slots_to_keys跳跃表来保存槽和键之间的关系。slots_to_keys跳跃表每个节点的分值保存键对应的槽位,每个节点的成员都是一个数据库键,如下图所示: +![image.png](../static/12609483-df3aa06eac8be3cc.png) +![image.png](../static/12609483-daa1017965210d79.png) + +##### 重新分片 +Redis集群的重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点。 +重新分片操作可以在线(online)进行,在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。 +“重新分片的实现原理 +Redis集群的重新分片操作是由Redis的集群管理软件redis-trib负责执行的,Redis提供了进行重新分片所需的所有命令,而redis-trib则通过向源节点和目标节点发送命令来进行重新分片操作。 +redis-trib对集群的单个槽slot进行重新分片的步骤如下: +1)redis-trib对目标节点发送CLUSTER SETSLOTIMPORTING命令,让目标节点准备好从源节点导入(import)属于槽slot的键值对。 +2)redis-trib对源节点发送CLUSTER SETSLOTMIGRATING命令,让源节点准备好将属于槽slot的键值对迁移(migrate)至目标节点。 +3)redis-trib向源节点发送CLUSTER GETKEYSINSLOT命令,获得最多count个属于槽slot的键值对的键名(key name)。 +“重新分片的实现原理 +Redis集群的重新分片操作是由Redis的集群管理软件redis-trib负责执行的,Redis提供了进行重新分片所需的所有命令,而redis-trib则通过向源节点和目标节点发送命令来进行重新分片操作。 +redis-trib对集群的单个槽slot进行重新分片的步骤如下: +1)redis-trib对目标节点发送CLUSTER SETSLOTIMPORTING命令,让目标节点准备好从源节点导入(import)属于槽slot的键值对。 +2)redis-trib对源节点发送CLUSTER SETSLOTMIGRATING命令,让源节点准备好将属于槽slot的键值对迁移(migrate)至目标节点。 +3)redis-trib向源节点发送CLUSTER GETKEYSINSLOT命令,获得最多count个属于槽slot的键值对的键名(key name)。 +4)对于步骤3获得的每个键名,redis-trib都向源节点发送一个MIGRATE0命令,将被选中的键原子地从源节点迁移至目标节点。 +5)重复执行步骤3和步骤4,直到源节点保存的所有属于槽slot的键值对都被迁移至目标节点为止。每次迁移键的过程如图17-24所示。 +6)redis-trib向集群中的任意一个节点发送CLUSTER SETSLOTNODE命令,将槽slot指派给目标节点,这一指派信息会通过消息发送至整个集群,最终集群中的所有节点都会知道槽slot已经指派给了目标节点。 +![image.png](../static/12609483-9cef67ecb52a18dc.png) + +#####槽迁移相关 + +``` +typedef struct clusterState { + clusterNode *importing_slots_from[16384]; + clusterNode *migrating_slots_to[16384]; +} clusterState; +``` +clusterState结构的importing_slots_from数组记录了当前节点正在从其他节点导入的槽,如果importing_slots_from[i]的值不为NULL,而是指向一个clusterNode结构,那么表示当前节点正在从clusterNode所代表的节点导入槽i。同理migrating_slots_to数组记录了当前节点正在导出到其他节点的槽。 +当对集群进行重新分片时,源节点会对migrating_slots_to数组进行更新,目标节点会对importing_slots_from数组进行更新。 + +#####ASK错误 +在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:属于被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面。 +当客户端向源节点发送一个与数据库键有关的命令,并且命令要处理的数据库键恰好就属于正在被迁移的槽时: +·源节点会先在自己的数据库里面查找指定的键,如果找到的话,就直接执行客户端发送的命令。 +·相反地,如果源节点没能在自己的数据库里面找到指定的键,那么这个键有可能已经被迁移到了目标节点,源节点将向客户端返回一个ASK错误,指引客户端转向正在导入槽的目标节点,在向目标节点发送之前想要执行的命令之前,需要先发送ASKING命令,将客户端的REDIS_ASKING标识打开,否则目标节点不会对正在迁移的槽执行相关的命令。(客户端的REDIS_ASKING标识是一个一次性标识,当节点执行了一个带有REDIS_ASKING标识的客户端发送的命令之后,客户端的REDIS_ASKING标识就会被移除。) +ASK +![image.png](../static/12609483-0b76181613ab9b88.png) +![image.png](../static/12609483-a5913adb192d42b8.png) + +#####ASK错误和MOVED错误的区别 +ASK错误和MOVED错误都会导致客户端转向,它们的区别在于: +·MOVED错误代表槽的负责权已经从一个节点转移到了另一个节点:在客户端收到关于槽i的MOVED错误之后,客户端每次遇到关于槽i的命令请求时,都可以直接将命令请求发送至MOVED错误所指向的节点,因为该节点就是目前负责槽i的节点。 +·与此相反,ASK错误只是两个节点在迁移槽的过程中使用的一种临时措施:在客户端收到关于槽i的ASK错误之后,客户端只会在接下来的一次命令请求中将关于槽i的命令请求发送至ASK错误所指示的节点,但这种转向不会对客户端今后发送关于槽i的命令请求产生任何影响,客户端仍然会将关于槽i的命令请求发送至目前负责处理槽i的节点,除非ASK错误再次出现。 +#####复制和故障转移 +Redis集群中的节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点继续处理命令请求。 +##### 故障检测 +集群中的每个节点都会定期地向集群中的其他节点发送PING消息,以此来检测对方是否在线,如果接收PING消息的节点没有在规定的时间内,向发送PING消息的节点返回PONG消息,那么发送PING消息的节点就会将接收PING消息的节点标记为疑似下线(probable fail,PFAIL)。 +集群中的各个节点会通过互相发送消息的方式来交换集群中各个节点的状态信息,例如某个节点是处于在线状态、疑似下线状态(PFAIL),还是已下线状态。 +一个主节点A通过消息得知主节点B认为主节点C进入了疑似下线状态时,主节点A会在自己的clusterState.nodes字典中找到主节点C所对应的clusterNode结构,并将主节点B的下线报告(failure report)添加到clusterNode结构的fail_reports链表里面 +![image.png](../static/12609483-17b68c0d6e4a8c69.png) + +#####故障转移 +当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移的执行步骤: +1)复制下线主节点的所有从节点里面,会有一个从节点被选中。 +2)被选中的从节点会执行SLAVEOF no one命令,成为新的主节点。 +3)新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己。 +4)新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。 +5)新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。 +#####选举新的主节点 +新的主节点是通过选举产生的,这个选举新主节点的方法和第16章介绍的选举领头Sentinel的方法非常相似,因为两者都是基于Raft算法的领头选举(leader election)方法来实现的。 +以下是集群选举新的主节点的方法: +1)集群的配置纪元是一个自增计数器,它的初始值为0。 +2)当集群里的某个节点开始一次故障转移操作时,集群配置纪元的值会被增一。 +3)对于每个配置纪元,集群里每个负责处理槽的主节点都有一次投票的机会,而第一个向主节点要求投票的从节点将获得主节点的投票。 +4)当从节点发现自己正在复制的主节点进入已下线状态时,从节点会向集群广播一条CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到这条消息、并且具有投票权的主节点向这个从节点投票。 +5)“如果一个主节点具有投票权(它正在负责处理槽),并且这个主节点尚未投票给其他从节点,那么主节点将向要求投票的从节点返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示这个主节点支持从节点成为新的主节点。 +6)每个参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根据自己收到了多少条这种消息来统计自己获得了多少主节点的支持。 +7)如果集群里有N个具有投票权的主节点,那么当一个从节点收集到大于等于N/2+1张支持票时,这个从节点就会当选为新的主节点。 +8)因为在每一个配置纪元里面,每个具有投票权的主节点只能投一次票,所以如果有N个主节点进行投票,那么具有大于等于N/2+1张支持票的从节点只会有一个,这确保了新的主节点只会有一个。 +9)如果在一个配置纪元里面没有从节点能收集到足够多的支持票,那么集群进入一个新的配置纪元,并再次进行选举,直到选出新的主节点为止。 +#####消息 +集群中的各个节点通过发送和接收消息(message)来进行通信, +节点发送的消息主要有以下五种: +·MEET消息: +当发送者接到客户端发送的CLUSTER MEET命令时,发送者会向接收者发送MEET消息,请求接收者加入到发送者当前所处的集群里面。 +PING消息 +集群里的每个节点默认每隔一秒钟就会从已知节点列表中随机选出五个节点,然后对这五个节点中最长时间没有发送过PING消息的节点发送PING消息,以此来检测被选中的节点是否在线。除此之外,如果节点A最后一次收到节点B发送的PONG消息的时间,“距离当前时间已经超过了节点A的cluster-node-timeout选项设置时长的一半,那么节点A也会向节点B发送PING消息,这可以防止节点A因为长时间没有随机选中节点B作为PING消息的发送对象而导致对节点B的信息更新滞后。 +PONG消息 +当接收者收到发送者发来的MEET消息或者PING消息时,为了向发送者确认这条MEET消息或者PING消息已到达,接收者会向发送者返回一条PONG消息。另外,一个节点也可以通过向集群广播自己的PONG消息来让集群中的其他节点立即刷新关于这个节点的认识,例如当一次故障转移操作成功执行之后,新的主节点会向集群广播一条PONG“消息,以此来让集群中的其他节点立即知道这个节点已经变成了主节点,并且接管了已下线节点负责的槽。 +FAIL消息 +当一个主节点A判断另一个主节点B已经进入FAIL状态时,节点A会向集群广播一条关于节点B的FAIL消息,所有收到这条消息的节点都会立即将节点B标记为已下线。 +PUBLISH消息 +当节点接收到一个PUBLISH命令时,节点会执行这个命令,并向集群广播一条PUBLISH消息,所有接收到这条PUBLISH消息的节点都会执行相同的PUBLISH命令。 +### 第 18 章 发布与订阅 +在Redis中,客户端可以订阅一个或多个频道,之后其他客户端往这个频道发送消息时,所有订阅者都可以收到这条消息。 +SUBSCRIBE订阅一个频道。 +PSUBSCRIBE是订阅一个符合规则的一个或多个频道。 +PUBLISH是往频道发布一条消息。 +#####频道的订阅与退订 +当一个客户端执行SUBSCRIBE命令订阅某个或某些频道的时, +Redis将所有频道的订阅关系都保存在redisServer的pubsub_channels字典里面,这个字典的键是某个被订阅的频道,而键的值则是一个链表,链表里面记录了所有订阅这个频道的客户端,如下图所示 +![image.png](../static/12609483-3230f1f23b429264.png) +·client-1、client-2、client-3三个客户端正在订阅"news.it"频道。 +·客户端client-4正在订阅"news.sport"频道。 +·client-5和client-6两个客户端正在订阅"news.business"频道。 +退订频道 +UNSUBSCRIBE命令的行为和SUBSCRIBE命令的行为正好相反,当一个客户端退订某个或某些频道的时候,服务器将在pubsub_channels字典中找到频道对应的订阅者链表,然后从订阅者链表中删除退订客户端的信息。 + +##### 模式的订阅与退订 +服务器将所有频道的订阅关系都保存在服务器状态的pubsub_channels属性里面,与此类似,服务器也将所有模式的订阅关系都保存在服务器状态的pubsub_patterns属性里面,pubsub_patterns属性是一个链表,链表中的每个节点都包含着一个pubsub Pattern结构,这个结构的pattern属性记录了被订阅的模式,而client属性则记录了订阅模式的客户端。 +如下图所示: +![image.png](../static/12609483-2cc427780dabbd24.png) +展示了一个pubsub_patterns链表示例,这个链表记录了以下信息: +·客户端client-7正在订阅模式"music.*"。 +·客户端client-8正在订阅模式"book.*"。 +·客户端client-9正在订阅模式"news.*"。 +退订模式 +模式的退订命令PUNSUBSCRIBE是PSUBSCRIBE命令的反操作:当一个客户端退订某个或某些模式的时候,服务器将在pubsub_patterns链表中查找并删除那些pattern属性为被退订模式,并且client属性为执行退订命令的客户端的pubsubPattern结构。 +发送消息 +当一个Redis客户端执行PUBLISH命令将消息message发送给频道channel的时候,服务器需要执行以下两个动作: +1)将消息message发送给channel频道的所有订阅者。 +2)如果有一个或多个模式pattern与频道channel相匹配,那么将消息message发送给pattern模式的订阅者。 +将消息发送给频道订阅者 +因为服务器状态中的pubsub_channels字典记录了所有频道的订阅关系,所以为了将消息发送给channel频道的所有订阅者,PUBLISH命令要做的就是在pubsub_channels字典里找到频道channel的订阅者名单(一个链表),然后将消息发送给名单上的所有客户端。 + +##### 查看订阅信息 +PUBSUB命令是Redis 2.8新增加的命令之一,客户端可以通过这个命令来查看频道或者模式的相关信息,比如某个频道目前有多少订阅者,又或者某个模式目前有多少订阅者。 +PUBSUB CHANNELS[pattern] +这个命令用于返回服务器当前被订阅的频道(如果没有pattern参数,那么命令返回服务器当前被订阅的所有频道) +PUBSUB NUMSUB +PUBSUB NUMSUB[channel-1 channel-2...channel-n]子命令接受任意多个频道作为输入参数,并返回这些频道的订阅者数量。 +PUBSUB NUMPAT +PUBSUB NUMPAT子命令用于返回服务器当前被订阅模式的数量。 +这个子命令是通过返回pubsub_patterns链表的长度来实现的,因为这个链表的长度就是服务器中,使用订阅模式订阅频道的客户端的数量。 +##### 第19章 事务 +Redis可以使用事务来将多个命令打包成一条命令,然后一次性,按顺序地在服务端执行,在事务执行期间,服务器不会中断事务去执行其他客户端的命令请求。 +以下是一个事务执行的过程,该事务首先以一个MULTI命令为开始,接着将多个命令放入事务当中,最后由EXEC命令将这个事务提交(commit)给服务器执行: +``` +redis> MULTI +OK +redis> SET "name" "Practical Common Lisp" +QUEUED +redis> GET "name" +QUEUED +redis> SET "author" "Peter Seibel" +QUEUED +redis> GET "author" +QUEUED +redis> EXEC +1) OK +2) "Practical Common Lisp" +3) OK +4) "Peter Seibel +``` +####事务的实现 +#####事务的开始 +MULTI命令的执行标志着事务的开始,MULTI命令可以将执行该命令的客户端从非事务状态切换至事务状态,这一切换是通过改变redisClient的flags属性中的REDIS_MULTI标识来完成的。 +#####命令入队 +当一个客户端处于非事务状态时,这个客户端发送的命令会立即被当一个客户端切换到事务状态之后,服务器会根据这个客户端发来的不同命令执行不同的操作: +·如果客户端发送的命令为EXEC、DISCARD、WATCH、MULTI四个命令的其中一个,那么服务器立即执行这个命令。 +·与此相反,如果客户端发送的命令是EXEC、DISCARD、WATCH、MULTI四个命令以外的其他命令,那么服务器并不立即执行这个命令,而是将这个命令放入一个事务队列里面,然后向客户端返回QUEUED回复。 +服务器判断命令是该入队还是该立即执行的过程如下图所示: +![image.png](../static/12609483-21d0ef9d804fa018.png) + +#####事务队列 +redisClient中保存了自己的事务状态,这个事务状态保存在客户端状态的mstate属性里面: +``` +typedef struct redisClient { +//事务状态 + multiState mstate; /* MULTI/EXEC state */ + // ... +} redisClient; +//事务状态包含一个事务队列,以及一个已入队命令的计数器(也可以说是事务队列的长度) +typedef struct multiState { + // 事务队列,FIFO顺序 + multiCmd *commands; + // 已入队命令计数 + int count; +} multiState; +//事务队列是一个multiCmd类型的数组,数组中的每个multiCmd结构都保存了一个已入队命令的相关信息,包括指向命令实现函数的指针、命令的参数,以及参数的数量: +typedef struct multiCmd { + // 参数 + robj **argv; + // 参数数量 + int argc; + // 命令指针 + struct redisCommand *cmd; +} multiCmd; +``` +事务队列以先进先出(FIFO)的方式保存入队的命令,较先入队的命令会被放到数组的前面,而较后入队的命令则会被放到数组的后面。 +#####执行事务 +当一个处于事务状态的客户端向服务器发送EXEC命令时,这个EXEC命令将立即被服务器执行。服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行命令所得的结果全部返回给客户端。 +#####WATCH命令的实现 +WATCH命令是一个乐观锁(optimistic locking),它可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过了,如果是的话,服务器将拒绝执行事务,并向客户端返回代表事务执行失败的空回复。 +乐观锁与悲观锁 +悲观锁就是假定最坏的情况,每次取数据时,都假设数据会被修改,所以取数据时都进行加锁,这样其他线程取数据时就会阻塞,直到获取到锁,然后取数据。java中的synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。 +乐观锁就是假定最好的情景,每次取数据时,都假设数据不会被修改,只有真正修改数据时会判断之前取到的值与变量当前在内存中的值是否一致,只有一致时才进行更新,否则就不更新。一般通过版本号或者CAS算法实现(CAS算法就是当且仅当当前内存中的的值等于预期值时,CAS通过原子方式用新值来更新内存中的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试) +乐观锁适合写比较少的场景,悲观锁适合写比较多的场景。 +乐观锁的缺点 +1.ABA问题,就是变量先被改成B值,然后改成A值,JDK1.5的AtomicStampedReference的compareAndSet会判断对象的引用是否相同,相同才进行更新。 +2. 循环时间长开销大,执行不成功会一直重试,长时间执行会给CPU带来压力 +3.只能保证一个共享变量的原子操作,多个共享变量时 CAS 无效。但是JDK 1.5以后,提供了AtomicReference类来保证了引用对象之间的原子性,可以将多个变量放在同一个对象中,来保证原子性。 +#####使用WATCH命令监视数据库键 +每个Redis数据库都保存着一个watched_keys字典,这个字典的键是某个被WATCH命令监视的数据库键,而字典的值则是一个链表,链表中记录了所有监视相应数据库键的客户端。 +``` +typedef struct redisDb { + // 正在被WATCH命令监视的键 + dict *watched_keys; +} redisDb; +``` +下图是一个watched_keys字典的示例,从这个watched_keys字典中可以看出: +·客户端c1和c2正在监视键"name"。 +·客户端c3正在监视键"age"。 +·客户端c2和c4正在监视键"address"。 +![image.png](../static/12609483-b683204e668de0d9.png) + +##### 监视机制的触发 +所有对数据库进行修改的命令,比如SET、LPUSH、SADD、ZREM、DEL、FLUSHDB等等,在执行之后都会调用multi.c/touchWatchKey函数对watched_keys字典进行检查,查看是否有客户端正在监视刚刚被命令修改过的数据库键,如果有的话,那么touchWatchKey函数会将监视被修改键的客户端的REDIS_DIRTY_CAS标识打开,表示该客户端的事务安全性已经被破坏。 +##### 判断事务是否安全 +当服务器接收到一个客户端发来的EXEC命令时,服务器会根据这个客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行事务: +·如果客户端的REDIS_DIRTY_CAS标识已经被打开,那么说明客户端所监视的键当中,至少有一个键已经被修改过了,在这种情况下,客户端提交的事务已经不再安全,所以服务器会拒绝执行客户端提交的事务,否则服务器将执行客户端提交的事务。 +#####事务的ACID性质 +在传统的关系式数据库中,常常用ACID性质来检验事务功能的可靠性和安全性。在Redis中,事务总是具有原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation),并且当Redis运行在某种特定的持久化模式下时,事务也具有耐久性(Durability)。 +##### 原子性 +事务具有原子性指的是,数据库将事务中的多个操作当作一个整体来执行,服务器要么就执行事务中的所有操作,要么就一个操作也不执行。 +对于Redis的事务功能来说,事务队列中的命令要么就全部都执行,要么就一个都不执行,因此,Redis的事务是具有原子性的。 +Redis的事务和传统的关系型数据库事务的最大区别在于,Redis不支持事务回滚机制(rollback),即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止。(原书作者认为,Redis事务的执行时错误通常都是编程错误产生的,所以没必要) +##### 一致性 +事务具有一致性指的是,如果数据库在执行事务之前是一致的,那么在事务执行之后,无论事务是否执行成功,数据库也应该仍然是一致的。Redis事务的一致性主要在于解决一些错误,防止事务执行破坏数据库。例如入队错误,执行错误,服务器停机。 +服务器停机 +如果Redis服务器在执行事务的过程中停机,那么根据服务器所使用的持久化模式,可能有以下情况出现: +·如果服务器运行在无持久化的内存模式下,那么重启之后的数据库将是空白的,因此数据总是一致的。 +·如果服务器运行在RDB模式下,那么在事务中途停机不会导致不一致性,因为服务器可以根据现有的RDB文件来恢复数据,从而将数据库还原到一个一致的状态。如果找不到可供使用的RDB文件,那么重启之后的数据库将是空白的,而空白数据库总是一致的。 +·如果服务器运行在AOF模式下,那么在事务中途停机不会导致不一致性,因为服务器可以根据现有的AOF文件来恢复数据,从而将数据库还原到一个一致的状态。如果找不到可供使用的AOF文件,那么重启之后的数据库将是空白的,而空白数据库总是一致的。 +综上所述,无论Redis服务器运行在哪种持久化模式下,事务执行中途发生的停机都不会影响。 +##### 隔离性 +事务的隔离性指的是,即使数据库中有多个事务并发地执行,各个事务之间也不会互相影响,并且在并发状态下执行的事务和串行执行的事务产生的结果完全相同。 +因为Redis使用单线程的方式来执行事务(以及事务队列中的命令),并且服务器保证,在执行事务期间不会对事务进行中断,因此,Redis的事务总是以串行的方式运行的,并且事务也总是具有隔离性的。 +##### 耐久性 +事务的耐久性指的是,当一个事务执行完毕时,执行这个事务所得的结果已经被保存到永久性存储介质(比如硬盘)里面了,即使服务器在事务执行完毕之后停机,执行事务所得的结果也不会丢失。 +### 第 21 章 排序 +在Redis中使用SORT命令可以对列表键、集合键或者有序集合键的值进行排序。 +##### SORT命令的实现 +SORT命令为每个被排序的键都创建一个与键长度相同的数组,数组的每个项都是一个redisSortObject结构,obj指针会指向列表或集合中的单个元素,score代表分值,根据score来对数组进行排序(因为涉及到字符串的排序及其他复杂情况,所以才单独使用score存储分值,而不是直接取元素的值) +以下是redisSortObject结构的完整定义: + ``` +typedef struct _redisSortObject { + // 被排序键的值 + robj *obj; + // 权重 + union { + // 排序数字值时使用 + double score; + // 排序带有BY选项的字符串值时使用 + robj *cmpobj; + } u; +} redisSortObject; + ``` +服务器执行SORT命令的步骤: +1)创建一个和numbers列表长度相同的数组,该数组的每个项都是一个redis.h/redisSortObject结构,如图所示 +![image.png](../static/12609483-73f1c52b4c03b738.png) + +2)遍历数组,将各个数组项的obj指针分别指向numbers列表的各个项,构成obj指针和列表项之间的一对一关系,如图21-2所示。 +![image.png](../static/12609483-36a4169adca877d7.png) + +3)遍历数组,将各个obj指针所指向的列表项转换成一个double类型的浮点数,并将这个浮点数保存在相应数组项的u.score属性里面,如图所示。 +![image.png](../static/12609483-15f831546f662ac9.png) +4)根据数组项u.score属性的值,对数组进行数字值排序,排序后的数组项按u.score属性的值从小到大排列,如图所示。 +![image.png](../static/12609483-072e5f8dfe85b67a.png) +5)遍历数组,将各个数组项的obj指针所指向的列表项作为排序结果返回给客户端,程序首先访问数组的索引0,返回u.score值为1.0的列表项"1";然后访问数组的索引1,返回u.score值为2.0的列表项"2";最后访问数组的索引2,返回u.score值为3.0的列表项"3"。 + +##### ALPHA选项的实现 +通过使用ALPHA选项,SORT命令可以对包含字符串值的键进行排序,会根据字母表的顺序来对键进行排序。 +##### ASC选项和DESC选项的实现 +在默认情况下,SORT命令执行ASC升序排序,排序后的结果按值的大小从小到大排列。ASC升序排序和DESC降序排序都由相同的快速排序算法执行,它们之间的不同之处在于: +·在执行升序排序时,排序算法使用的对比函数产生升序对比结果。 +·而在执行降序排序时,排序算法所使用的对比函数产生降序对比结果。 +##### BY选项的实现 +通过使用BY选项,SORT命令可以指定某些字符串键,或者某个哈希键所包含的某些域(field)来作为元素的权重,对一个键进行排序。 +以下这个例子就使用苹果、香蕉、樱桃三种水果的价钱,对集合键fruits进行了排序,排序时会根据BY选项所给定的模式*-price,查找相应的权重键,例如对于"apple"元素,查找程序返回权重键"apple-price"。 +``` +redis> MSET apple-price 8 banana-price 5.5 cherry-price 7 +OK +redis> SORT fruits BY *-price +1) "banana" +2) "cherry" +3) "apple” +``` +#####LIMIT选项的实现 +可以通过LIMIT选项,我们可以让SORT命令只返回其中一部分已排序的元素。 +LIMIT选项的格式为LIMIT: +·offset参数表示要跳过的已排序元素数量。 +·count参数表示跳过给定数量的已排序元素之后,要返回的已排序元素数量。 +##### GET选项的实现 +通过使用GET选项,我们可以让SORT命令在对键进行排序之后,根据被排序的元素,以及GET选项所指定的模式,查找并返回其他键的值。 +#####STORE选项的实现 +在默认情况下,SORT命令只向客户端返回排序结果,而不保存排序结果: +``` +redis> SADD students "peter" "jack" "tom" +(integer) 3 +redis> SORT students ALPHA +1) "jack" +2) "peter" +3) "tom" +``` +但是,通过使用STORE选项,我们可以将排序结果保存在指定的键里面,并在有需要时重用这个排序结果。 +#####选项的执行顺序 +如果按照选项来划分的话,一个SORT命令的执行过程可以分为以下四步: +1)排序:在这一步,命令会使用ALPHA、ASC或DESC、BY这几个选项,对输入键进行排序,并得到一个排序结果集。 +2)限制排序结果集的长度:在这一步,命令会使用LIMIT选项,对排序结果集的长度进行限制,只有LIMIT选项指定的那部分元素会被保留在排序结果集中。 +3)获取外部键:在这一步,命令会使用GET选项,根据排序结果集中的元素,以及GET选项指定的模式,查找并获取指定键的值,并用这些值来作为新的排序结果集。 +4)保存排序结果集:在这一步,命令会使用STORE选项,将排序结果集保存到指定的键上面去。 +5)向客户端返回排序结果集:在最后这一步,命令遍历排序结果集,并依次向客户端返回排序结果集中的元素。 +### 第 22 章 二进制位数组 +位数组是一个数组,数组元素是一个二进制位,在Redis中可以使用字符串对象来代表二进制位数组,字符串对象是一个SDS结构,SDS结构的buf是一个char数组,数组的每一位是一个char字符,每个char字符是8位。 +![image.png](../static/12609483-3b85e8021cd8e8d0.png) + +#####GETBIT命令的实现 +GETBIT命令用于返回位数组bitarray在offset偏移量上的二进制位的值。 +GETBIT命令的执行过程如下: +1)计算byte= offset÷8」,byte值记录了offset偏移量指定的二进制位保存在位数组的哪个字节。 +2)计算bit=(offset mod 8)+1,bit值记录了offset偏移量指定的二进制位是byte字节的第几个二进制位。 +3)根据byte值和bit值,在位数组bitarray中定位offset偏移量指定的二进制位,并返回这个位的值。 +##### SETBIT命令的实现 +同理,SETBIT用于将位数组bitarray在offset偏移量上的二进制位的值设置为value,并向客户端返回二进制位被设置之前的旧值。 +#####BITCOUNT命令的实现 +BITCOUNT命令用于统计给定位数组中,值为1的二进制位的数量。 +实现BITCOUNT命令可能使用的几种算法进行介绍: +1.遍历算法,对每个位进行遍历,统计1的个数,时间复杂度为O(N) +2.查表算法,因为8个二进制位的1和0的排列方式是有限的,可以进行建表,将所有情况进行计算好。然后每次获取8个二进制位,然后进行查表比对,获取到这8个二进制位中1的个数。时间复杂度为O(N/8)。键长为8位的表如图所示,但是需要占用一定的内存空间来存储表。 +![image.png](../static/12609483-025e4bf14ce8dc40.png) +3.二进制位统计算法(3):variable-precision SWAR算法 +“swar函数每次执行可以计算32个二进制位的汉明重量,它比之前介绍的遍历算法要快32倍,比键长为8位的查表法快4倍,比键长为16位的查表法快2倍,并且因为swar函数是单纯的计算操作,所以它无须像查表法那样,使用额外的内存。复杂度为O(N/32)。 +“另外,因为swar函数是一个常数复杂度的操作,所以我们可以按照自己的需要,在一次循环中多次执行swar,从而按倍数提升计算汉明重量的效率: +·例如,如果我们在一次循环中调用两次swar函数,那么计算汉明重量的效率就从之前的一次循环计算32位提升到了一次循环计算64位。 + +#####Redis中BITCOUNT的实现 +在执行BITCOUNT命令时,程序会根据未处理的二进制位的数量来决定使用那种算法: +·如果未处理的二进制位的数量大于等于128位,那么程序使用variable-precision SWAR算法来计算二进制位的汉明重量。在variable-precision SWAR算法方面,BITCOUNT命令在每次循环中载入128个二进制位,然后调用四次32位variable-precision SWAR算法来计算这128个二进制位的汉明重量。 +·如果未处理的二进制位的数量小于128位,那么程序使用查表算法来计算二进制位的汉明重量。 +##### BITOP命令的实现 +因为C语言直接支持对字节执行逻辑与(&)、逻辑或(|)、逻辑异或(^)和逻辑非(~)操作,所以BITOP命令的AND、OR、XOR和NOT四个操作都是直接基于这些逻辑操作实现的。 +例如: +``` +BITOP AND result x y +``` +使用BITOP命令对x和y进行与运算,并将结果保存在result键上。 +### 第 23 章 慢查询日志 +Redis的慢查询日志功能用于记录执行时间超过给定时长的命令请求,用户可以通过这个功能产生的日志来监视和优化查询速度。 +服务器配置有两个和慢查询日志相关的选项: +·slowlog-log-slower-than选项指定执行时间超过多少微秒(1秒等于1 000 000微秒)的命令请求会被记录到日志上。 +·slowlog-max-len选项指定服务器最多保存多少条慢查询日志。如果满了会将最旧的慢查询日志删除。 +### 第 24 章 监视器 +通过执行MONITOR命令,客户端可以将自己变为一个监视器,实时地接收并打印出服务器当前处理的命令请求的相关信息。 +![image.png](../static/12609483-e9060f6bdd9b95fa.png) +服务器收到MONITOR命令后,会将这个客户端的REDIS_MONITOR标志会被打开,并且这个客户端本身会被添加到monitors链表的表尾,之后服务器在每次处理命令请求之前,都会调用replicationFeedMonitors函数,由这个函数将被处理的命令请求的相关信息发送给各个监视器。 +![image.png](../static/12609483-47571d940363c993.png) \ No newline at end of file diff --git a/docs/RedisDataStruct.md b/docs/RedisDataStruct.md new file mode 100644 index 0000000..5cd40d6 --- /dev/null +++ b/docs/RedisDataStruct.md @@ -0,0 +1,279 @@ +#### [1.Redis常见的数据结构有哪些?](#Redis常见的数据结构有哪些?) + +#### [2.谈一谈你对Redis中简单动态字符串的理解?](#谈一谈你对Redis中简单动态字符串的理解?) + +#### [3.谈一谈你对Redis中hash对象的理解?](#谈一谈你对Redis中hash对象的理解?) +#### [4.谈一谈你对Redis中List的理解?](#谈一谈你对Redis中List的理解?) +#### [5.谈一谈你对Redis中Set的理解?](#谈一谈你对Redis中Set的理解?) +#### [6.谈一谈你对Redis中有序集合ZSet的理解?](#谈一谈你对Redis中有序集合ZSet的理解?) + +### Redis常见的数据结构有哪些? + +Redis中主要的数据结构有字符串,Hash哈希表,List列表,Set集合,ZSet有序集合。 +### 谈一谈你对Redis中简单动态字符串的理解? +Redis中的简单动态字符串其实是对C语言中的字符串的封装和优化, + +##### 因为C语言的字符串有两个缺点: + +1.不是二进制安全的(因为字符串以空字符作为结束的标志,字符串中间不能有空字符,Redis的简单动态字符串是一个结构体,有一个变量存储了字符串的长度,所以不需要以空字符作为结束的标志)。 + +从定义上来看,二进制安全是一种主要用于字符串操作函数相关的计算机编程术语。一个二进制安全功能(函数),其本质上将操作输入作为原始的、无任何特殊格式意义的数据流。对于每个字符都公平对待,不特殊处理某一个字符。 + +简单来说C语言使用\0字符来作为字符串结束符是不符合这种二进制安全的规范的。 + +2.频繁修改一个字符串时,会涉及到内存的重分配,比较消耗性能。(Redis中的简单动态字符串会有内存预分配和惰性空间释放)。 + +**如果字符串实际使用长度len<1M**,实际分配空间=len长度来存储字符串+1字节存末尾空字符+len长度的预分配空闲内存 + +**如果字符串实际使用长度len>1M**,实际分配空间=len长度来存储字符串+1字节存末尾空字符+1M长度的预分配空闲内存 + +所以Redis中的简单动态字符串结构,除了包含一个字符数组的属性,还包含数组的长度,数组的实际使用长度等属性,通过增加长度属性,可以保证字符串是二进制安全的,从而可以保存任意类型的数据,例如一张图片,对象序列化后的数据等等。 + +##### 字符串使用场景如下: + +1.字符串可以保存一些字符串数据,也可以保存一些数字类型的数据,所以可以使用INCR, DECR, INCRBY对数字进行加减,所以可以把字符串当成计数器使用。 + +2.同时因为在C语言中,每个字符是一个字节,是8个二进制位,所以可以把简单动态字符串作为一个位数组来使用,通过setbit,getbit命令来对位数组进行赋值,取值,可以以很小的空间来保存用户一年的每日签到数据,以及Redis中的布隆过滤器也是通过位数组来实现的。 + +##### 字符串的底层存储 + +在Redis中,每一个Value都是一个Redis对象,对应的都是RedisObject结构,在RedisObject结构中,保存了对象的类型type,底层的编码encoding等一些属性,也拥有一个ptr指针,指向对象具体的存储地址。 +``` +struct RedisObject { + int4 type; //类型 + int4 encoding; //编码 + int24 lru; + int32 refcount; + void *ptr; +} robj; +``` +在Redis中,字符串有两种存储方式,int编码,embstr编码和raw编码。 +##### int编码 + +当value是一个整数,并且可以使用long类型(8字节)来表示时,那么会属于int编码,ptr直接存储数值。(并且Redis会进行优化,启动时创建0~9999的字符串对象作为共享变量。) +##### embstr和raw编码 +两种存储方式下,都RedisObject和SDS结构(简单动态字符串)来存储字符串,区别在于,embstr对象用于存储较短的字符串,embstr编码中RedisObject结构与ptr指向的SDS结构在内存中是连续的,内存分配次数和内存释放次数均是一次,而raw编码会分别调用两次内存分配函数来分别创建RedisObject结构和SDS结构。 + +### 谈一谈你对Redis中hash对象的理解? +在Redis中,value可以是一个hash表,底层编码可以是ziplist,也可以是hashtable(默认情况下,当元素小于512个时,底层使用ziplist存储数据) +#### ziplist + +元素保存的字符串长度较短且元素个数较少时(小于64字节,个数小于512),出于节约内存的考虑,hash表会使用ziplist作为的底层实现,ziplist是一块连续的内存,里面每一个节点保存了对应的key和value,然后每个节点很紧凑地存储在一起,优点是没有冗余空间,缺点插入新元素需要调用realloc扩展内存。(可能会进行内存重分配,将内容拷贝过去,也可能在原有地址上扩展)。 + +#### hashtable +元素比较多时就会使用hashtable编码来作为底层实现,这个时候RedisObject的ptr指针会指向一个dict结构,dict结构中的ht数组保存了ht[0]和ht[1]两个元素,通常使用ht[0]保存键值对,ht[1]只在渐进式rehash时使用。hashtable是通过链地址法来解决冲突的,table数组存储的是链表的头结点(添加新元素,首先根据键计算出hash值,然后与数组长度取模之后得到数组下标,将元素添加到数组下标对应的链表中去)。 +``` +struct dict { + int rehashindex; + dictht ht[2]; +} +struct dictht { + dictEntry** table; // 二维 + long size; // 第一维数组的长度 + long used; // hash 表中的元素个数 ... +} +typedef struct dictEntry { + //键 + void *key; + //值,可以是一个指针,也可以是一个uint64_t整数,也可以是int64_t的整数 + union { + void *val; + uint64_tu64; + int64_ts64; + } v; + //指向下一个节点的指针 + struct dictEntry *next; +} dictEntry; +``` + +#### 渐进式rehash + +进行当负载因子>=1时,会进行哈希表扩展操作(如果是在执行BGSAVE或BGREWRITEAOF命令期间,那么需要>=5才会进行扩展)。 + +进行当负载因子<0.1时,会进行哈希表收缩操作。 + +因为直接一次性完成rehash会对性能产生影响,所以可以渐进式rehash,具体执行步骤是: + +##### 初始化 +1.首先将对dict结构中ht[1]哈希表分配空间(大小取决于最接近实际使用空间的2的n次方幂),然后将rehashindex属性设置为0。 + +##### 转移 +2.这种渐进式rehash转移元素不是全部转移,如果在rehash时全部转移,可能会导致rehash期间Redis实例无法处理后面的请求,所以采取的策略是每次用到某个下标下的键值对才进行转移这个下标下所有的键值对。然后每次对ht[0]中的元素查找,修改,添加时,除了执行指定操作外,还会将对应下标的所有键值对rehash到ht[1],并且会将rehashindex+1。 + +##### 更改指针指向 +3.当ht[0]中所有键值对都是rehash到ht[1]中后,那么会ht[1]和ht[0]的指针值进行交换,将rehashindex设置为-1,代表rehash完成。 +(整个rehash期间,查找,更新,删除会先去ht[0]中执行,没有才会到ht[1]中执行,新添加键值对是会被保存到ht[1]中)。 + +### 谈一谈你对Redis中List的理解? + +在Redis中,存储的value可以是一个列表List,跟Java中的LinkedList很像,底层数据结构是一个链表,插入和删除很快,随机访问较慢,时间复杂度是O(N)。Java中的列表数据进行缓存时一般是序列化成JSON,以字符串的形式存储在Redis上,而不是使用Redis中的List来进行存储。Redis中的List可以作为一个队列来使用,也可以作为一个栈来使用。在实际使用中,常用来做异步队列使用,将可以延时处理的任务序列化成字符串塞进Redis的列表,另外一个线程从列表中轮询数据进行处理 +#### quicklist + +老版本中的Redis,元素较少时,使用ziplist来作为底层编码,元素较多时使用双向链表linkedList作为底层编码。因为链表每个节点需要prev,next指针,需要占用16字节,而且每个节点内存都是单独分配,加剧内存碎片化,所以新版本使用quiklist作为底层编码,quicklist的是一个双向链表,但是它的每一个节点是一个ziplist。(默认每个ziplist最大长度为8k字节) + +### 谈一谈你对Redis中Set的理解? +Set是一个无序的,不重复的字符串集合,底层编码有inset和hashtable两种。 + +##### inset + +当元素都为整数,且元素个数较少时会使用inset作为底层编码,inset结构中的有一个contents属性,content是是一个整数数组,从小到大保存了所有元素。 + +##### hashtable + +当元素个数较多时,Set使用hashtable来保存元素,元素的值作为key,value都是NULL。 + +### 谈一谈你对Redis中有序集合ZSet的理解? + +Zset与Set的区别在于每一个元素都有一个Score属性,并且存储时会将元素按照Score从低到高排列。底层是通过跳跃表实现的。 + +##### ziplist + +当元素较少时(元素个数<128个,且每个元素的长度小于64字节),ZSet的底层编码使用ziplist实现,所有元素按照Score从低到高排序。 + +##### skiplist+dict + +当元素较多时,使用skiplist+dict来实现。 +skiplist存储元素的值和Score,并且将所有元素按照分值有序排列。便于以O(logN)的时间复杂度插入,删除,更新,及根据Score进行范围性查找。 + +dict存储元素的值和Score的映射关系,便于以O(1)的时间复杂度查找元素对应的分值。 + +### 布隆过滤器是什么? +布隆过滤器可以理解为一个有误差的set结构,使用布隆过滤器来判断元素是否存在其中时, + +如果返回结果是存在,实际可能存在也可能不存在, + +返回结果不存在时,实际结果肯定是不存在。 + +布隆过滤器实际上是一个大型的位数组,添加key时,通过几个hash函数对key计算得到多个hash值,将每个hash值与布隆过滤器的位数组的size取模得到下标,然后将数组中这些下标位置的值都设置为1。 + +创建key为userid的布隆过滤器,0.01是误判率,10000是初始大小 + +``` +127.0.0.1:6379> bf.reserve userid 0.01 100000 +``` + +调用exist指令判断181920是否存在于布隆过滤器,如果返回0,不存在,那么说明一定不存在,如果返回1,代表可能存在,也可能不存在。 + +``` +127.0.0.1:6379> bf.add userid '181920' +(integer) 1 +``` + +参考链接: + +https://blog.csdn.net/lifetragedy/article/details/103945885 + +### 谈一谈你对跳跃表的理解? + +就是单链表的查找的时间复杂度为O(N),为了提高查询效率,可增加一些索引节点,让查询时间复杂度降低为O(logN)。(只有底层单链表会保存节点数据,上层的节点之后保存几个索引项和分数,也就是向左指向前一个节点的索引,向右指向后一个节点的索引,向下指向上一级的索引。) + +时间复杂度的推理过程可以认为,从每一级索引中两个节点中选一个节点作为下一级索引的节点,让下一级索引的节点数量为本级索引节点数量的一半。假设原始链表的长度为n,第一级索引节点个数为n/2,第二级为n/4,一直到最高层。 +``` +假设h为层数,f(h)为该层索引节点数量。 +f(h)=n/(2^h) +而最高层的索引节点会是只有两个索引节点。 +也就是f(maxH)=n/(2^maxH)=2,所以最大层数maxH= log(n) - 1层 ,加上单链表的层数,总层数也就是log(n)层。 +例如我们的单链表是1-19的,建立的多层索引如下, +如果我们要查找19,一开始在第四层遍历1,发现19>1,向右走,发现19>9,那么需要到上一级索引去找,然后发现19>13,继续到下一层,一直到第一层,发现19>17,然后往右走遍历17,18,19 +所以每一层遍历的节点其实是 +1 9 +9 13 +13 15 17 +17 18 19 +所以每一层最多遍历的节点数是<=3,这是由于每一层索引节点数是上一层的节点数的一半来得到的,每两个节点的区间,在上一层中都是两个区间,就是第三层的 1 5区间对应上一层的1 3 区间和3 5 区间。 +所以时间复杂度=总层数*每层遍历节点数=3logN,去掉常数后是log(N) + +第四层 1 9 +第三层 1 5 9 13 +第二层 1 3 5 7 9 11 13 15 17 +第一层 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +``` + +![image-20200802160611901](../static/image-20200802160611901.png) + + + +![查找的时间复杂度证明.jpeg](../static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDEyNTc5,size_16,color_FFFFFF,t_70.jpeg) + +但是这种全索引的结构有缺点,就是之后在插入新节点时,是没有办法为新节点添加一些新的索引节点的。所以有可能会导致一个小区间内有很多个节点,查找时近似于单链表,这样时间复杂度就变成O(N)了,所以Redis对跳跃表做了一些优化,可以简单认为会计算出一个随机数,代表这个添加添加的索引层数,然后进行添加。同时假设节点拥有k层索引的概率f(k),节点拥有k-1层索引结构的概率为f(k-1),f(k) = f(k-1)*P,假设p为1/2,那么也就是f(k)=f(k-1)\*0.5,那么第k层的索引节点数也就是为k-1层的0.5倍。最终索引结构跟上面的这种全索引结构是类似的,只是索引节点不一定分布均匀。空间复杂度=每一层索引节点数=n+n/2+n/4….=2n。Redis做了优化,发现P取1/4时,时间复杂度跟P取1/2是一样的,并且空间复杂度更低。 + +```java +//ZSKIPLIST_P代表获取下一层的概率,是0.25 +//ZSKIPLIST_MAXLEVEL代表是最大层数,是32层 +int zslRandomLevel(void) { + int level = 1; + while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) + level += 1; + return (level server.aof_rewrite_min_size(默认为1MB)。 +4. 当前AOF文件大小和最后一次AOF重写后的大小之间的比率大于等于指定的增长百分比(默认为1倍,100%,也就是当前AOF文件大小>=上次重写后文件的2倍后) + +### Redis持久化策略该如何进行选择? +RDB持久化的特点是: +文件小,恢复快,不影响性能,实时性低,兼容性差(老版本的Redis不兼容新版本的RDB文件) +AOF持久化的特点是: +文件大,恢复慢,性能影响大,实时性高。是目前持久化的主流(主要是当前项目开发不太能接受大量数据丢失的情况)。 +需要了解的是持久化选项的开启必然会造成一定的性能消耗。 + +两种持久化方式的缺点: + +RDB持久化主要在于bgsave在进行fork操作时,会阻塞Redis的主线程。以及向硬盘写数据会有一定的I/O压力。 + +AOF持久化主要在于将aof_buf缓冲区的数据同步到磁盘时会有I/O压力,而且向硬盘写数据的频率会高很多。其次是,AOF文件重写跟RDB持久化类似,也会有fork时的阻塞和向硬盘写数据的压力。 + +#### 以下是几种持久化方案选择的场景: + +##### 1.不需要考虑数据丢失的情况 +那么不需要考虑持久化。 + +##### 2.单机实例情况下 + +可以接受丢失十几分钟及更长时间的数据,可以选择RDB持久化,对性能影响小,如果只能接受秒级的数据丢失,只能选择AOF持久化。 + +##### 3.在主从环境下 + +因为主服务器在执行修改命令后,会将命令发送给从服务器,从服务进行执行后,与主服务器保持数据同步,实现数据热备份,在master宕掉后继续提供服务。同时也可以进行读写分离,分担Redis的读请求。 + +那么在从服务器进行数据热备份的情况下,是否还需要持久化呢? +需要持久化,因为不进行持久化,主服务器,从服务器同时出现故障时,会导致数据丢失。(例如:机房全部机器断电)。如果系统中有自动拉起机制(即检测到服务停止后重启该服务)将master自动重启,由于没有持久化文件,那么master重启后数据是空的,slave同步数据也变成了空的。应尽量避免“自动拉起机制”和“不做持久化”同时出现。 + +所以一般可以采用以下方案: + +主服务器不开启持久化,使得主服务器性能更好。 + +从服务器开启AOF持久化,关闭RDB持久化,并且定时对AOF文件进行备份,以及在凌晨执行bgaofrewrite命令来进行AOF文件重写,减小AOF文件大小。(当然如果对数据丢失容忍度高也可以开启RDB持久化,关闭AOF持久化) + +##### 4.异地灾备 + +一般性的故障(停电,关机)不会影响到磁盘,但是一些灾难性的故障(地震,洪水)会影响到磁盘,所以需要定时把单机上或从服务器上的AOF文件,RDB文件备份到其他地区的机房。 + +### 什么是AOF文件追加阻塞? + +修改命令添加到aof_buf之后,如果配置是everysec,那么会有一个线程每秒执行fsync操作,调用write写入磁盘一次,但是如果此时来了很多Redis请求,Redis主线程持续高速向aof_buf写入命令,硬盘的负载可能会越来越大,IO资源消耗更快,fsync操作可能会超过1s,aof_buf缓冲区堆积的命令会越来越多,所以Redis的处理逻辑是会对比上次fsync成功的时间,如果超过2s,则主线程阻塞直到fsync同步完成,所以最多可能丢失2s的数据,而不是1s。(每当 AOF 追加阻塞事件发生时,在 info Persistence 统计中,aof_delayed_fsync 指标会累加,查看这个指标方便定位 AOF 阻塞问题。) + +### 什么是RDB-AOF混合持久化? + +redis4.0相对与3.X版本其中一个比较大的变化是4.0添加了新的混合持久化方式。前面已经详细介绍了AOF持久化以及RDB持久化,这里介绍的混合持久化就是同时结合RDB持久化以及AOF持久化混合写入AOF文件。这样做的好处是可以结合 rdb 和 aof 的优点, 快速加载同时避免丢失过多的数据,缺点是 aof 里面的 rdb 部分就是压缩格式不再是 aof 格式,可读性差。 + +#### 开启混合持久化 + +4.0版本的混合持久化默认关闭的,通过aof-use-rdb-preamble配置参数控制,yes则表示开启,no表示禁用,默认是禁用的,可通过config set修改。 + +#### 混合持久化过程 + +了解了AOF持久化过程和RDB持久化过程以后,混合持久化过程就相对简单了。 + +混合持久化同样也是通过BGREWRITEAOF完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,如下图: + +![img](../static/1075473-20180726181756270-1907770368.png) + + + +#### 数据恢复 + +当我们开启了混合持久化时,启动redis依然优先加载aof文件,aof文件加载可能有两种情况如下: + +- aof文件开头是rdb的格式, 先加载 rdb内容再加载剩余的 aof。 +- aof文件开头不是rdb的格式,直接以aof格式加载整个文件。 + +#### 优缺点 + +优点: + +1. 混合持久化结合了RDB持久化和 AOF 持久化的优点, 由于绝大部分都是RDB格式,生成的AOF文件体积更小,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。 + +缺点: + +1. 兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差 \ No newline at end of file diff --git a/docs/RedisUserful.md b/docs/RedisUserful.md new file mode 100644 index 0000000..2695ea5 --- /dev/null +++ b/docs/RedisUserful.md @@ -0,0 +1,207 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +下面是主要是自己看完《Redis设计与实现》,《Redis深度历险:核心原理与应用实践》后,为了更好得掌握Redis,网上找了一些面试题,查阅书籍和资料后,写的解答。 + +#### [1.Redis主从同步是怎么实现的?](#Redis主从同步是怎么实现的?) + +#### [2.Redis中哨兵是什么?](#Redis中哨兵是什么?) + +#### [3.客户端是怎么接入哨兵系统的?](#客户端是怎么接入哨兵系统的?) +#### [4.Redis哨兵系统是怎么实现自动故障转移的?](#Redis哨兵系统是怎么实现自动故障转移的?) +#### [5.谈一谈你对Redis Cluster的理解?](#谈一谈你对RedisCluster的理解?) +#### [6.RedisCluster是怎么实现数据分片的?](#RedisCluster是怎么实现数据分片的?) +#### [7.RedisCluster是怎么做故障转移和发现的?](#RedisCluster是怎么做故障转移和发现的?) + +### Redis主从同步是怎么实现的? + +主从节点建立连接后,从节点会进行判断: + +1.如果这是从节点之前没有同步过数据 + +属于初次复制,会进行**全量重同步**,那么从节点会向主节点发送PSYNC?-1 命令,请求主节点进行**全量重同步**。 + +2.如果从节点不是初次复制(例如出现掉线后重连) +这个时候从节点会将之前进行同步的Replication ID(一个随机字符串,标识主节点上的特定数据集)和offset(从服务器当前的复制偏移量)通过PSYNC id offset命令发送给主节点,主节点会进行判断, + +* 如果Replication ID跟当前的Replication ID不一致(可能主节点进行了变化),或者是当前buffer缓冲区中不存在对应的offset,那么会跟上面的初次复制一样,进行**全量重同步**。 +* 如果Replication ID跟当前的Replication ID一致并且当前buffer缓冲区中存在对应的offset,那么会进行**部分重同步**。(部分重同步是Redis 2.8之后的版本支持的,主要基于性能考虑,为了断线期间的小部分数据修改进行全量重同步效率比较低) + +##### 全量重同步 +主节点会执行BGSAVE命令,fork出一个子进程,在后台生成一个RDB持久化文件,完成后,发送给从服务器,从节点接受并载入RDB文件,使得从节点的数据库状态更新至主节点执行BGSAVE命令时的状态。并且在生成RDB文件期间,主节点也会使用一个缓冲区来记录这个期间执行的所有写命令,将这些命令发送给从节点,从节点执行命令将自己数据库状态更新至与主节点完全一致。 + +##### 部分重同步 +因为此时从节点只是落后主节点一小段时间的数据修改,并且偏移量在复制缓冲区buffer中可以找到,所以主节点把从节点落后的这部分数据修改命令发送给从节点,完成同步。 + +##### 命令传播 +在执行全量重同步或者部分重同步以后,主从节点的数据库状态达到一致后,会进入到命令传播阶段。主节点执行修改命令后,会将修改命令添加到内存中的buffer缓冲区(是一个定长的环形数组,满了时就会覆盖前面的数据),然后异步地将buffer缓冲区的命令发送给从节点。 + +### Redis中哨兵是什么? +Redis中的哨兵服务器是一个运行在哨兵模式下的Redis服务器,核心功能是监测主节点和从节点的运行情况,在主节点出现故障后,完成自动故障转移,让某个从节点升级为主节点。 +### 客户端是怎么接入哨兵系统的? +**配置提供者:**前者只负责存储当前最新的主从节点信息,供客户端获取。 + +**代理**:客户端所有请求都会经过哨兵节点。 + +首先Redis中的哨兵节点是一个配置提供者,而不是代理。因为客户端只是在首次连接时从哨兵节点获取主节点信息,后续直接与主节点进行连接,发送请求,接收请求结果。 + +具体流程: + +```java + String masterName = "mymaster"; + Set sentinels = new HashSet<>(); + sentinels.add("192.168.92.128:26379"); + sentinels.add("192.168.92.128:26380"); + + JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels); //初始化过程做了很多工作 + Jedis jedis = pool.getResource(); + jedis.set("key1", "value1"); + pool.close(); +``` +在实际开发中,通过在客户端配置哨兵节点的地址+主节点的名称(哨兵系统可能会监控多个主从节点,名称用于区分)就可以与哨兵节点建立连接,获取到主节点信息,然后与主节点建立连接,并且订阅哨兵节点的频道,以便在主节点变化后,接受到通知。 +上面的代码在底层实现是客户端向依次向哨兵节点发送"sentinel get-master-addr-by-name"命令,成功获得主节点信息就不向后面的哨兵节点发送命令。同时客户端会订阅哨兵节点的+switch-master频道,一旦主节点发送故障,哨兵服务器对主节点进行自动故障转移,会将从节点升级主节点,并且更新哨兵服务器中存储的主节点信息,会向+switch-master频道发送消息,客户端得到消息后重新从哨兵节点获取主节点信息,初始化连接池。 + +### Redis哨兵系统是怎么实现自动故障转移的? +##### 1.认定主节点主观下线 +因为每隔2s,哨兵节点会给主节点发送PING命令,如果在一定时间间隔内,都没有收到回复,那么哨兵节点就认为主节点主观下线。 +##### 2.认定主节点客观下线 +哨兵节点认定主节点主观下线后,会向其他哨兵节点发送sentinel is-master-down-by-addr命令,获取其他哨兵节点对该主节点的状态,当认定主节点下线的哨兵数量达到一定数值时(这个阀值是Sentinel配置中quorum参数的值,通常我们设置为哨兵总节点数的1/2),就认定主节点客观下线。 +##### 3.进行领导者哨兵选举 +认定主节点客观下线后,各个哨兵之间相互通信,选举出一个领导者哨兵,由它来对主节点进行故障转移操作。 + +选举使用的是Raft算法,基本思路是所有哨兵节点A会先其他哨兵节点,发送命令,申请成为该哨兵节点B的领导者,如果B还没有同意过其他哨兵节点,那么就同意A成为领导者,最终得票超过半数以上的哨兵节点会赢得选举,如果本次投票,没有选举出领导者哨兵,那么就开始新一轮的选举,直到选举出哨兵节点(实际开发中,最先判定主节点客观下线的哨兵节点,一般就能成为领导者。) +##### 4.领导者哨兵进行故障转移 + 领导者哨兵节点首先会从从节点中选出一个节点作为新的主节点。选择的规则是: + +* 1.首先排除一些不健康的节点。(下线的,断线的,最近5s没有回复哨兵节点的INFO命令的,与旧的主服务器断开连接时间较长的) + +* 2.然后根据优先级,复制偏移量,runid最小,来选出一个从节点作为主节点。 + +向这个从节点发送slaveof no one命令,让其成为主节点,通过slaveof 命令让其他从节点成为它的从节点,将已下线的主节点更新为新的主节点的从节点,将其他从节点的复制目标改完新的主节点,将旧的主服务器改为从服务器。 + +### 谈一谈你对RedisCluster的理解? + +当需要存储的数据量特别大,单个Redis实例无法满足需求,所以需要分片,早期很多业务就是在业务中进行分片,通过自定义一些业务规则,将不同的数据存储在不同的Redis实例中。后来就有了官方推出的集群化方案Redis Cluster。 + +### RedisCluster是怎么实现数据分片的? + +首先Redis Cluster设定了有16384个槽位,然后根据启动时集群的主节点数量进行均分,每个主节点得到一定数量的槽位,为了保证每个主节点挂掉之后,服务保持高可用,一般会为每个主节点配置几个从节点,从节点保存了主节点上同步过来的数据,一旦主节点挂掉,会有一个从节点会被选为主节点。客户端在与Redis Cluster建立连接时会获取到各槽位与主节点之间的映射关系,然后缓存到本地。 + +客户端执行命令的流程: + +假设客户端需要发送一个查询请求时,首先会对key使用CRC16算法计算得到一个hash值,然后将hash值与16384(也就是2的14次方)进行取模(下面是网上找的图,应该是CRC16(key)%16384),得到一个槽位slot,然后根据本地缓存的槽位映射关系表,找到这个槽位slot对应的主节点,发送查询命令。主节点在收到命令后会有以下几种情况: + +**1.这个主节点确实负责这个槽位,且不在迁移中。** + +直接查询到这个键值对,返回给客户端。 + +**2.这个主节点不负责这个槽位,或者已经确定转移到其他节点上去了(Moved指令)** + +可能是这个槽位已经迁移了,或者是客户端将指令发送到了错误的节点,或者是客户端缓存的槽位映射关系以前过期。主节点就会给客户端返回Moved指令及正确的节点信息,Moved指令相当于是一个永久重定向指令,用于纠正客户端缓存的错误槽位信息。客户端收到后会更新本地的槽位关系表,然后向正确的节点发送查询指令。 + +**3.这个槽位正在迁移中(ASKING指令)** + +如果这个槽位之前是在这个主节点上,但是目前正在迁移(槽位状态为IMPORTING),那么如果现在主节点上存在这个可以,就成功处理请求。否则就返回ASKING指令+槽位所在的新节点,ASKING指令相当于是一个临时重定向指令,客户端收到之后不会更新本地的槽位关系表,只是将本次请求发送到新节点。 + +![图片](../static/640-20210124192752928) + +#### Redis Cluster的节点扩容和下线 + +##### 扩容 + +例如数据量太大了,原有的节点太少了,希望增加一些Redis实例,分担一些数据量。在Redis Cluster中,需要程序员手动执行命令,将节点添加到集群,并执行命令从其他的主节点上分配一些槽位到这个新节点上。 + +具体执行命令流程如下: + +``` +./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000 +``` + +可以看到.使用**addnode**命令来添加节点,第一个参数是新节点的地址,第二个参数是任意一个已经存在的节点的IP和端口。 + +新节点现在已经连接上了集群, 成为集群的一份子, 并且可以对客户端的命令请求进行转向了, 但是和其他主节点相比, 新节点还有两点区别: + +- 新节点没有包含任何数据, 因为它没有包含任何哈希槽位. +- 尽管新节点没有包含任何哈希槽位, 但它仍然是一个主节点, 所以在集群需要将某个从节点升级为新的主节点时, 这个新节点不会被选中。 + +接下来, 只要使用 redis-trib 程序, 将集群中的某些哈希槽位移动到新节点里面, 新节点就会成为真正的主节点了。 + +槽位迁移需要执行的命令会比较的多,大家想了解的可以看看这篇文章: + +https://www.cnblogs.com/youngchaolin/archive/2004/01/13/12034660.html + + + +![图片](../static/640-20210124203435521) + +##### 下线 + +在节点上执行 redis-trib.rb del-node{host:port} {donwNodeId} 通知其他的节点,自己下线,如果本节点是主节点,会安排对应的从节点阶梯主节点的位置。 + +#### RedisCluster是怎么做故障转移和发现的? + +##### 1.主观下线 + +当节点 1 向节点 2 例行发送 Ping 消息的时候,如果节点 2 正常工作就会返回 Pong 消息,同时会记录节点 1的相关信息,更新与节点2的最近通讯时间。如果节点 1的定时任务检测到与节点 2 上次通讯的时间超过了 cluster-node-timeout 的时候,就会更新本地节点状态,把节点 2 更新为主观下线。 + +##### 2.客观下线: + +由于 Redis Cluster 的节点不断地与集群内的节点进行通讯,下线信息也会通过 Gossip 消息传遍所有节点。 + +因此集群内的节点会不断收到下线报告,当半数以上持有槽的主节点标记了某个节点是主观下线时,便会认为节点2**客观下线**,执行后面的流程。 + +##### 3.资格检查 + +每个从节点都会检查与主节点断开的时间。如果这个时间超过了 cluster-node-timeout*cluster-slave-validity-factor(从节点有效因子,默认为 10),那么就没有故障转移的资格。也就是说这个从节点和主节点断开的太久了,很久没有同步主节点的数据了,不适合成为新的主节点,因为成为新的主节点以后,其他的从节点回同步它的数据。 + +##### 4.从节点触发选举 + +通过资格的从节点都可以触发选举。但是触发选举是有先后顺序的,这里按照复制偏移量的大小来判断。 + +这个偏移量记录了执行命令的字节数。主服务器每次向从服务器传播 N 个字节时就会将自己的复制偏移量+N,从服务在接收到主服务器传送来的 N 个字节的命令时,就将自己的复制偏移量+N。 + +复制偏移量越大说明从节点延迟越低,也就是该从节点和主节点沟通更加频繁,该从节点上面的数据也会更新一些,因此复制偏移量大的从节点会率先发起选举。 + +##### 5.从节点发起选举 + +首先每个主节点会去更新配置纪元(clusterNode.configEpoch),这个值是不断增加的整数。 + +在节点进行 Ping/Pong 消息交互时也会更新这个值,它们都会将最大的值更新到自己的配置纪元中。 + +这个值记录了每个节点的版本和整个集群的版本。每当发生重要事情的时候,例如:出现新节点,从节点精选。都会增加全局的配置纪元并且赋给相关的主节点,用来记录这个事件。 + +说白了更新这个值目的是,保证所有主节点对这件“大事”保持一致。大家都统一成一个配置纪元(一个整数),表示大家都知道这个“大事”了。 + +更新完配置纪元以后,每个从节点会向集群内发起广播选举的消息。 + +##### 6.主节点为选举投票 + +参与投票的只有主节点,从节点没有投票权。每个主节点在收到从节点请求投票的信息后,如果它还没有为其他从节点投票,那么就会把票投给从节点。(也就是主节点的票只会投给第一个请求它选票的从节点。) + +超过半数的主节点通过某一个节点成为新的主节点时投票完成。 + +如果在 cluster-node-timeout*2 的时间内从节点没有获得足够数量的票数,本次选举作废,进行第二轮选举。 + +这里每个候选的从节点会收到其他主节点投的票。在第2步领先的从节点通常此时会获得更多的票,因为它触发选举的时间更早一些。 + +获得票的机会更大,也是由于它和原主节点延迟少,理论上数据会更加新一点。 + +##### 7.选举完成 + +当满足投票条件的从节点被选出来以后,会触发替换主节点的操作。新的主节点别选出以后,删除原主节点负责的槽数据,把这些槽数据添加到自己节点上。 + +并且广播让其他的节点都知道这件事情,新的主节点诞生了。 + +#### Redis Cluster的主从复制模型 + +Redis集群的架构就是多个主节点,每个主节点负责一部分槽位,每个主节点拥有几个从节点,一旦主节点挂掉,会挑选一个从节点成为新的主节点,负责这部分槽位。如果某个主节点和它的所有从节点都挂掉了,那么这部分槽位就不可用。 + +#### Redis Cluster一致性 + +CAP理论认为C一致性,A可用性,P分区容错性,一般最多只能满足两个,也就是只能满足CA和CP,而Redis Cluster的主从复制的模式是异步复制的模式,也就是主节点执行修改命令后,返回结果给客户端后,有一个异步线程会一直从aof_buf缓冲区里面取命令发送给从节点,所以不是一种强一致性,只满足CAP理论中的CA。 + +参考链接: + +http://www.redis.cn/topics/cluster-tutorial.html + +https://blog.csdn.net/g6u8w7p06dco99fq3/article/details/105336857 + diff --git a/docs/Rule.md b/docs/Rule.md new file mode 100644 index 0000000..0d5e10a --- /dev/null +++ b/docs/Rule.md @@ -0,0 +1,78 @@ +##### 1.使用BigDecimal替代Float和Double + +主要Float和Double是使用类似于科学计数法那样"有效数字+指数"来表示的,所以在二进制存储时,是会丢失精度,没法做到精准的。所以浮点数之间不能使用==等值判断,浮点数包装类型之间不能使用equals + +```java +float a = 1.0f - 0.9f; +float b = 0.9f - 0.8f; //通过debug调试发现这两次减运算减下来,a和b的值存在一些差异,不是一模一样的,丢失了精度 +Boolean result = a == b;//结果是false + +Float x = 1.0F - 0.9F; +Float y = 0.9F - 0.8F; +Boolean result = x.equals(y);//结果是false +``` + +解决方案: + +1. 任何货币金额,均以最小货币单位且整型类型来进行存储。例如在数据库里面存10000,代表是100.00元。 + +2. 使用Bigdecimal来存这些值,并且进行加减。 + +```java +BigDecimal a1 = new BigDecimal("1.0"); +BigDecimal b1 = new BigDecimal("0.9"); +BigDecimal c1 = new BigDecimal("0.8"); + +BigDecimal a2 = a1.subtract(b1); +BigDecimal b2 = b1.subtract(c1); + +System.out.println(a2.equals(b2));//打印结果是true +``` + + + +2.布尔类型的变量,都不要加is前缀,否则部分框架解析会引起序列化错误。 + +一般假设我们定义一个Boolean变量为isHot,按照Bean规范生成的get方法应该是isIsHot,但是我们使用IDEA来自动生成get方法时,生成的是isHot()方法, + +> JavaBeans规范中对这些均有相应的规定,基本数据类型的属性,其getter和setter方法是getXXX()和setXXX,但是对于基本数据中布尔类型的数据,又有一套规定,其getter和setter方法是isXXX()和setXXX。但是包装类型都是以get开头。 + +```java +private boolean isHot; +public boolean isHot() { + return isHot; +} +public void setHot(boolean hot) { + isHot = hot; +} +``` + +这样子在实例对象转json时,序列化框架看到isHot()方法会去找hot实例变量,这样就会找不到变量值。 + +在与前端交互时,同意需要注意,避免Boolean类型参数是is前缀开头的,因为Spring MVC在接受参数时,看到前端json传过来的值是isHot,而set方法中没有setIsHot()方法,只有setHot()方法 + +https://www.cnblogs.com/goloving/p/13086151.html + +https://blog.csdn.net/qq_31145141/article/details/71597608 + +##### 正确的加锁方式 + +通过这种方式来加锁,保证抛出异常时可以正常解锁,同时lock()方法不能在try代码块中调用,防止线程还没有加上锁时抛出异常,然后进行解锁,抛出IllegalMonitorStateException异常(对未加锁对象调用释放锁tryRelease()方法就会抛这个异常)。 + +```java +Lock lock = new XxxLock(); // ... +lock.lock(); +try { + doSomething(); + doOthers(); +} finally { + lock.unlock(); +} +``` + +##### COUNT(*)与count(列名) + +1.【强制】不要使用count(列名)或count(常量)来替代count(*),count(*)是SQL92定义的标 准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。 + 说明:count(*)会统计值为 NULL 的行,而 count(列名)会将NULL值排除掉,不会统计此列为 NULL 值的行。 + +2.count(distinct col) 计算该列除 NULL 之外的不重复行数 \ No newline at end of file diff --git a/docs/Spring.md b/docs/Spring.md new file mode 100644 index 0000000..fb814cf --- /dev/null +++ b/docs/Spring.md @@ -0,0 +1,335 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +#### [1.SpringAOP是怎么实现的?](#SpringAOP是怎么实现的?) +### SpringAOP是怎么实现的? + +Spring Aop是一种可以减少大量重复代码的一种编程技术,可以设置一个切面,比如说是某个包下面的所有方法,这些方法在执行的时候就会调用我们写的拦截方法,我们可以做一些类似于日志打印等一些操作。 + +实现AOP有三种方式:静态代理,使用JDK的Proxy类实现动态代理,使用CGLIB实现动态代理。 + +#### 静态代理 + +就是在代码里面创建一个代理类,实现目标类的接口,目标对象是代理类的成员变量,外界需要执行方法时,调用代理类的方法,代理类的方法内部先执行额外的操作,日志记录等,然后再调用目标类的方法。 + +```java +public interface IUserDao { + void save(); +} +public class UserDao implements IUserDao { + public void save() { + System.out.println("已经保存数据..."); + } +} +//代理类 +public class UserDaoProxy implements IUserDao { + private IUserDao target; + public UserDaoProxy(IUserDao iuserDao) { + this.target = iuserDao; + } + public void save() { + System.out.println("开启事务..."); + target.save(); + System.out.println("关闭事务..."); + } +} +``` + +### JDK动态代理 + +通过调用Proxy.newProxyInstance()方法可以为目标类创建一个代理类,然后调用代理类的方法时会调用InvocationHandler的invoke()方法,然后我们可以在invoke()方法里面做一些日志记录之类的额外操作,然后再调用真正的实现方法,也就是目标类的方法。 + +目标类必须有对应的接口类,我们拦截的方法必须是接口中定义的方法。 + +```java +public class Test implements TestInterface { + public void test(Integer a) { + System.out.printf("111111"); + } +} +public interface TestInterface { + void test(Integer a); +} +//创建一个类,继承InvocationHandler,重写invoke()方法,在这个方法里面做一些日志打印的操作后,然后通过反射的API调用method.invoke(target, args);本来的方法。 +public static class CustomInvocationHandler implements InvocationHandler { + Object target; + public CustomInvocationHandler(Object target) { + this.target = target; + } + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + System.out.println("before"); + //调用本来的方法 + Object result = method.invoke(target, args); + System.out.println("after"); + return null; + } +} +``` + + +```java +Test test = new Test(); +TestInterface proxyInstance = (TestInterface) Proxy.newProxyInstance(test.getClass().getClassLoader(), test.getClass().getInterfaces(), new CustomInvocationHandler(test)); +proxyInstance.test(11); +``` + +实现原理:就是在调用Proxy.newProxyInstance()时会根据类加载器和目标类Class对象动态创建一个代理类出来,动态代理类的所有方法的实现都是向下面这样,方法内部都是调用invocationHandler的invoke()方法 + +```java + public final void test(){ + throws + { + try + { + /* h就是handler,m3是Method对象,在静态代码块里面有一行这样的代码 + m3 = Class.forName("com.proxy.main.Test").getMethod("test", new Class[0]); + */ + this.h.invoke(this, m3, null); + return; + } + catch (RuntimeException localRuntimeException) + { + throw localRuntimeException; + } + catch (Throwable localThrowable) + { + } + throw new UndeclaredThrowableException(localThrowable); + } +``` + +### CGLIB动态代理 + +CGLIB主要是通过创建一个代理类,继承原来的类,并且重写父类的方法,然后将代理类实例返回,后续调用代理类的方法时,先执行一些额外的AOP相关的记录操作,然后再去执行父类的方法。(由于在Java中子类只能继承父类的非private的属性和方法,所以**由CGLIB创建的代理类,不会包含父类中的final或者private修饰的方法,**aop也无法捕获private相关的方法) + +创建一个类,继承MethodInterceptor类,重写intercept方法,接受方法调用。创建一个Enhancer实例,设置代理类的父类为目标类,设置回调。 + +```java +public static void main(final String[] args) { + System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/ruiwendaier/Downloads/testaop"); + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(Test.class); + enhancer.setCallback(new MyMethodInterceptor()); + Test test1 = (Test)enhancer.create(); + test1.test(); + +} +public static class MyMethodInterceptor implements MethodInterceptor { + @Override + public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { + System.out.println("Before: " + method.getName()); + Object object = methodProxy.invokeSuper(o, objects); + System.out.println("After: " + method.getName()); + return object; + } +} +public static class Test implements TestInterface { + public void test() { + System.out.printf("111111"); + } +} +``` + +生成的代理类中,对于父类中每一个能够继承重写的方法,动态代理类都会生成两个相应的方法。一个是方法内是直接调用父类(也就是目标类的方法),一个是生成的对应的动态代理的方法,里面会先调用代理类设置的intercept回调方法,然后再调用父类的方法。在调用时,会直接先调用重写的方法。 + +```java +//代理生成的方法 直接调用的父类(目标类的方法) + final String CGLIB$test$0(){ + return super.test(); + } + + //方法重写 测试样例中就是调用的这里的test方法 + public final String test() + { + //判断目标类是否有设置回调:enhancer.setCallback(this); + MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0; + if (tmp4_1 == null){ + tmp4_1; + CGLIB$BIND_CALLBACKS(this); + } + MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0; + //设置了方法的回调则调用拦截器方法intercept + if (tmp17_14 != null) + return (String)tmp17_14.intercept(this, CGLIB$test$0$Method, CGLIB$emptyArgs, CGLIB$test$0$Proxy); + return super.test(); + } + +``` + +### 区别 + +目标类必须有对应的接口类,然后JDK动态代理动态创建了一个类,实现了接口中的方法,不能对没有接口类的普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类; +Java动态代理只能对有接口类的类进行代理,并且使用Java原生的反射API进行操作,在生成类上比较高效,但是执行会效率低一些。CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效。 + +参考链接: + +https://blog.csdn.net/gyshun/article/details/81000997 + +### Spring IOC是什么? + +IOC就是invention of control,就是控制反转,将对象获取外界依赖资源的方式反转了。假设一个对象A依赖于对象B,在没有IOC以前,就是在对象A需要使用对象B时,去用代码显式地创建一个对象B。有了IOC以后,可以由Spring IOC容器来负责对象B的创建和销毁,创建后放在容器中,在对象A需要的时候再来取。 +DI(Dependency Injection,依赖注入)其实就是IOC的另外一种说法,就是IOC是通过依赖注入技术实现的。 +《跟我学spring3系列》https://www.iteye.com/blog/jinnianshilongnian-1413851 +https://www.cnblogs.com/xdp-gacl/p/4249939.html + +### Spring IOC是怎么解决循环依赖问题的? + +Spring IOC只能解决属性注入之间的循环依赖问题,如果是构造器之间的循环依赖,只会抛出BeanCurrentlyInCreationException异常。 + +Spring使用了3个Map来保存Bean,俗称为三级依赖: + +singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例,可以使用的。 + +earlySingletonObjects 二级缓存,bean刚刚构造完成,但是还没有进行属性填充。 + +singletonFactories 三级缓存,用于保存正在创建中的bean,以便于后面扩展有机会创建代理对象,此时的bean是没有完成属性填充的。 + +假设A类和B类相互依赖,A中有一个B类的属性,B中有一个A类的属性。那么在初始化A的Bean时,首先会依次去一级依赖,去二级依赖,三级依赖中去找,都没有就调用创建方法创建实例A,将A添加到三级依赖中,然后对A的属性进行依赖注入,填充属性时,发现B的Bean在各级依赖中都没有,就创建B的bean添加到三级依赖,然后对B的属性进行填充,填充B的属性A时,会从三级依赖中取出A,填充完放到二级依赖,然后对B进行初始化,初始化完成添加到一级依赖。B初始化完成后,将B从一级依赖中,填充到实例A,A可以进入到二级依赖,完全初始化完成后,A进入到一级依赖,供用户代码使用。 + +![](../static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark-20210316161015142.image) + + + +https://juejin.cn/post/6911692836714840077 + +https://segmentfault.com/a/1190000015221968 + +https://blog.csdn.net/qq_35165000/article/details/108185093?spm=1001.2014.3001.5501 + +### Bean的生命周期是怎么样的? + +Bean的生命周期主要分为以下四个阶段: + +1.**Bean的实例化阶段**-主要是在createBeanInstance()方法中,调用类的构造器方法来创建一个Bean实例。用户可以自定义一个类,继承InstantiationAwareBeanPostProcessorAdapter,重写它的两个方法,对Bean的实例化前后做一些额外的操作,例如打印日志。 + +```java +public class MyInstantiationAwareBeanPostProcessorAdapter extends InstantiationAwareBeanPostProcessorAdapter { + @Override + public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + if (beanName.equals("car")) { + System.out.println(beanName + "在实例化之前"); + } + return super.postProcessBeforeInstantiation(beanClass, beanName); + } + @Override + public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { + if (beanName.equals("car")) { + System.out.println(beanName + "在实例化之后"); + } + return super.postProcessAfterInstantiation(bean, beanName); + } +} +``` + +2.**属性赋值阶段**-主要是在populateBean()方法中,对Bean的各项属性进行赋值。 + +3.**Bean的初始化阶段**-主要调用用户自定义的初始化方法init-Method() + +用户可以自定义一个类,继承BeanPostProcessor,重写它的两个方法,对Bean的初始化前后做一些额外的操作,例如打印日志。 + +```java +public class NdBeanPostProcessor implements BeanPostProcessor { + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + System.out.println("NdBeanPostProcessor 在" + beanName + "对象初始化之前调用......"); + if (beanName.equals("car")) { + return new CglibInterceptor().getIntance(bean.getClass()); + } + return bean; + } + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + System.out.println("NdBeanPostProcessor 在" + beanName + "对象初始化之后调用......"); + return bean; + } +} +``` + +4.**Bean销毁阶段**,用户可以自定义destroyMethod()方法,在Bean被销毁时被调用。 + +### BeanFactory和FactoryBean有什么区别? + +**BeanFactory**是一个接口,定义了IOC容器的最基本的规范,并提供了IOC容器应遵守的的最基本的方法。在Spring代码中,BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,都是附加了某种功能的实现。 + +```java +package org.springframework.beans.factory; +import org.springframework.beans.BeansException; +public interface BeanFactory { + String FACTORY_BEAN_PREFIX = "&"; + Object getBean(String name) throws BeansException; + T getBean(String name, Class requiredType) throws BeansException; + T getBean(Class requiredType) throws BeansException; + Object getBean(String name, Object... args) throws BeansException; + boolean containsBean(String name); + boolean isSingleton(String name) throws NoSuchBeanDefinitionException; + boolean isPrototype(String name) throws NoSuchBeanDefinitionException; + boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException; + Class getType(String name) throws NoSuchBeanDefinitionException; + String[] getAliases(String name); +} +``` + +**FactoryBean**是一个接口,有一个创建bean对象的方法getObject(),当一些bean对象不能由ioc容器简单得调用类的构造器方法来创建实例对象时使用,可以将Bean类实现FactoryBean接口,实现getObject()方法,供ioc容器调用来创建bean对象。 + +```java +public interface FactoryBean { + @Nullable + T getObject() throws Exception; + + @Nullable + Class getObjectType(); + + default boolean isSingleton() { + return true; + } +} +``` + +https://www.cnblogs.com/aspirant/p/9082858.html + +### Springboot启动过程 + +##### 构造SpringApplication实例 + +1.首先会调用SpringApplication的静态方法run(),在这个方法里面会调用构造器方法创建出一个SpringApplication实例,在构造器中会确定当前web应用类型,是reactive web类型,还是servlet web类型,还是none类型。以及设置监听器等等,完成一些初始化操作。(监听器就是来监听SpringApplication启动过程的,在开始启动,创建上下文,启动失败等生命周期事件时都会调用监听器相关的方法) + +##### 执行run()方法 + +2.然后去执行实例的run()方法,首先会创建一个StopWatch计时器器,来统计run()方法的启动耗时,在日志里面会显示启动时间,那个时间就是在这里统计的。然后处理环境参数,就是`java -jar ***.jar`启动命令中带的那些jvm参数。 + +![img](../static/up-70a9e95e6c1208288334341bdb54bd59c17.png) + +##### 创建applicationContext + +3.会创建出一个ApplicationContext,一般servlet的应用的context类型是AnnotationConfigServletWebServerApplicationContext。(可以认为beanFactory就是ioc容器,但是我们一般不直接使用beanFactory获取bean,而是通过applicationContext来获取,ioc容器beanFactory是应用上下文applicationContext的一个属性,applicationContext也实现了BeanFactory接口,可以认为applicationContext是一个高级容器,applicationContext支持国际化,默认是启动时加载所有bean,而不是用到时才进行懒加载,以及支持事件机制。) + +##### 执行prepareContext()方法 + +4.然后会调用prepareContext()方法来为应用上下文做一些准备工作,会将运行时的参数封装成bean,注册到beanFactory中去,以及使用load方法加载启动类。 + +##### 执行refreshContext()方法 + +5.在这里会启动容器,也就是会为beanFactory做很多配置,注册BeanPostProcessors,设置类加载器等等。在这一步也会解析启动类中@SpringBootApplication这个组合注解。 + +##### afterRefresh()方法 + +6.这个方法里面会把容器里面所有ApplicationRunner自定义子类和CommandLineRunner自定义子类的Bean全部取出来,执行它们的run()方法。(就是有时候如果需要在应用启动后执行一些我们自定义的初始化操作,可以通过自定义一个类,继承ApplicationRunner类来实现。) + +之后会调用listeners.started()方法,通知所有Listener,application已经启动完成了,以及调用listeners.running()方法通知所有Listener,application已经运行了。 + +```java +//系统启动完可以做一些业务操作 +@Component +//如果有多个runner需要指定一些顺序 +@Order(1) +public class SimosApplicationRunner implements ApplicationRunner { +@Autowired +SystemInitService systemInitService; +@Override +public void run(ApplicationArguments args) throws Exception { + systemInitService.systemInit(); +} +} +``` + +https://my.oschina.net/funcy/blog/4873261 + +https://www.imooc.com/article/264722 \ No newline at end of file diff --git a/docs/SystemDesign.md b/docs/SystemDesign.md new file mode 100644 index 0000000..ff1b842 --- /dev/null +++ b/docs/SystemDesign.md @@ -0,0 +1,278 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +#### [1.怎么设计一个大文件上传的功能?](#怎么设计一个大文件上传的功能?) + +#### [2.谈一谈分布式ID生成方案的了解?](#谈一谈分布式ID生成方案的了解?) + +### 怎么设计一个大文件上传的功能? + +首先如果是大文件上传,考虑到网络不稳定容易造成上传失败,或者需要做断点续传功能,是不是对整个文件直接进行上传的。 +#### 1.计算hash + +首先是让前端对大文件计算hash值,主要是用于后端去判断这个文件在服务器上是否已经存在,其次是这个hash值可以作为上传文件的一个标识。 +如果是直接对整个文件计算hash值,文件过大时,可能会比较慢,通常是对文件进行抽样计算。根据hash值判断文件是否在后端已经存在,已存在就不进行上传了。 + +##### 影分身Hash +就是把文件切分成2M的分片,然后对首文件+中间每个文件的首中尾取2个字节+尾文件,进行合并,计算合并文件的MD5值,作为文件的hash值。 + +如果这个hash值在后端存在: +说明这个文件可能存在也可能不存在。准确性不是100%。 + +如果这个hash值在后端不存在: +说明这个文件一定不存在。准确性是100%。 + +通过这种方式判断的思路跟布隆过滤器比较像。 + +![img](../static/v2-e3634165fbe1c09983d564c34e2bbfb9_b.jpg) + +#### 2.分片上传 + +上传时不能一窝蜂地把所有分片文件发给后端,是把每个分片包装成一个网络请求,使用异步线程对从队列里面每次取一个分片进行上传。 +每个分片文件都有一个唯一标识,就是hash值+分片编号。这样如果上传中断了,下次再进行上传时,后端可以知道哪些分片已经上传,把未上传的分片唯一标识返回给前端,前端只需要上传上次未上传的分片,这也就是断点续传。 + +https://zhuanlan.zhihu.com/p/104826733 + +#### 3.合并请求 +有两种方案,一种是后端记录所有分片的上传状态,当所有分片全部上传完毕后,自动合并文件。另外一种就是前端发现传完所有文件后,调用接口通知后端去合并。 + + +### 谈一谈分布式ID生成方案的了解? +首先ID生成方案的技术考虑点: +唯一性:不能重复。 +趋势递增:保证id作为主键时,插入数据库时的顺序性,避免随机插入。 +单调递增:满足某些特殊业务的要求,保证后一秒请求生成的id比前一秒的大。 +信息安全性:不要像UUID一样泄露mac地址,也不要像数据库主键ID自增完全连续,泄露每日id生成数据量。 + +#### 1.UUID模式 + +太长,占用存储空间过大 +id是字符串类型,查询成本高于数字类型 +如果是包含mac地址的UUID会泄密 + +#### 2.单机数据库主键自增模式 + +id生成的量受限于单机MySQL数据库的性能 +强依赖于数据库,主从切换时容易导致重复发号 +容易泄露id生成量 + +#### 3.多机数据库主键自增模式 + +主要是将每个数据库的步长设置为一样,但是起始值不一样,以此错开生成的id,不便于扩展。 + +##### 4.Leaf号段模式模式 + +就是数据库存储一个maxId代表已经发放的id最大值,每次将maxId更新为max+step,取step数量的id发放。 +优点: +1.便于扩展,发号性能取决于step,可以动态调整。(Leaf做了Step动态调整策略,一个号段使用时间<15分钟,就让号段拥有的id量step翻倍(直到最大阀值100万),一个号段使用时间>30分钟,step减半(直到最低阀值,初始号段id量)。) +2.即便主节点的宕机,短时间Leaf也能继续提供服务,其次是主从切换时影响较小。 +缺点: +1.当号段里面的id用完时,会去数据库取新的号段,此时如果来了获取id的请求会需要进行等待。(Leaf做了双Buffer优化,使用了双Buffer各存储一个号段,当一个号段使用量达到10%后,就触发另一个号段去数据库取新号段进行更新,以便于当一个号段使用完时,可以直接切换到未使用的号段。) +2.id是连续的,容易泄密。(可以自定义抛弃策略,取号段时的时候抛弃一些id,或者定时抛弃掉一些id。) + +#### 5.Leaf-snowflake模式 + +就是沿用了snowflake原本的位数分配算法,1标志位+41位毫秒时间戳+10位机器位+12位序列号。使用zookeeper作为注册中心,id生成服务启动时,去指路径下获取所有节点的列表,判断当前ip+port是否有对应的workid存在,有就使用,没有就往插入一个新的永久顺序节点,序号则为workId(并且会将workid缓存到本地磁盘上)。运行期间每过3s,都会上报最新的时间戳到zookeeper。 +**时钟回滚的处理:** +**启动时:** +zookeeper里面会存上次生成id的时间戳,如果上次存储的时间戳>当前系统时间戳,那么就抛出异常,启动失败。 +**运行时:** +如果获取id时,发现上次生成id时的时间戳>当前系统时间戳,那么说明运行时发生了时钟回滚,如果回滚的时间差<5ms,就调用wait()方法等待10ms,然后再获取id,时间差>5ms,就抛出异常。 + +优点: + +**时钟回滚优化** + +1.对时钟回滚做了特殊处理。 + +**zookeeper弱依赖** + +2.为了减轻了zookeeper的弱依赖,实现在zookeeper挂了的情况下,id生成服务也能启动,每次启动后,在本地也缓存workid配置,一旦启动时,发现zookeeper连接不上,就通过从本地缓存配置中读取workid。(但是这样也有问题,本地缓存配置只存了workid,没有存上次生成id的最大时间戳,所以一旦启动前发生了时钟回滚,或者是修改了系统时间,这样从本地缓存配置中读取workid生成的id就可能是重复的。) + +**时间差优化** + +3.做了时间差优化,就是默认的时间戳是从1970年开始的,leaf是自己选定了2010年的一个时间点,以此来计算时间戳,这样可以在时间位数固定的情况下,增长服务最大运行时间。在毫秒时间戳为41位的情况下,时间差最大是69年,如果以1970年为起点,那么最大时间就是1970+69年,如果以2010年为起点就是2010+69年。 + +**序列号优化** + +4.为了防止生成的id的序列号部分都是从0开始,导致插入数据库时,有数据倾斜的问题,所以每次用新的毫秒时间戳时,序列号不是从0开始,而是计算一个0到100之间的随机数作为起点。 + +缺点: + +1.注册中心只支持Zookeeper + +2.潜在的时钟回拨问题 + +3.时间差过大时,生成id为负数。 + +### 6.百度的uid-generator模式 + +#### 默认模式 + +每次启动时向数据库插入一条数据,这行数据的主键是自增的,主键id就是workId, + +因为默认是snowflake算法是**1标志位+41位时间戳+10位机器号+12位序列号**, + +因为百度的是每次启动都获取新的机器号,所以它修改了这些位数配比,是 + +**1标志位+28位的秒级时间差+22位的机器号+13位的序列号**,所以总共支出2的22次方次启动,也就是400万次启动。最大服务支持时间是2*28次方,也就是8.7年。(优化点是修改位数分配,让服务时间更长,我们的位数分配是**30位秒级时间差+16位机器号+7位序列号**,最长服务时间支持34年,6.5w次机器启动,每个机器每秒128个并发,总共位数没有用到64位,只用到53位,这样生成的id转化为10进制更小。) + +解决时间回拨问题: + +* 启动时时间回拨 + +因为是每次都用新的机器号,所以当前机器号都是之前没有的,所以即便时间戳回拨也不影响。 + +* 运行时时间回拨 + +会使用lastSecond来记录上次生成id的时间戳,如果当前时间戳比lastSecond还小,就抛出异常。(优化点就是**回拨时间差较小时进行等待**,较长时再抛出异常。) + +缺点: + +1.默认的最大服务年限太短,只有8年。 + +2.回拨时间差较小时也是抛出异常,没有额外的判断逻辑。 + +3.没有像Leaf一样做序列号优化,可能生成的id序列号部分都是从0开始的多一些,可能会存在数据倾斜的问题。 + +#### 缓存模式 + +主要继承自默认模式,只是用一个环形数组来存储生成好的id,每次去环形数组中去,默认大小是2的13次方,8192。这种模式使用的时间取得不是实时的系统时间,而且使用启动时的时间,每次生成一组id时,对之前保存的时间+1。 + +**阀值检测预填充**:取id时,发现可用id数小于阀值50%时,就对后面已经使用的id进行再填充。 + +**定期填充**:每5分钟定期会去检查环形数组中id使用情况,然后生成一组最大序列号个数的id(默认是8192个),然后进行填充,多的直接丢弃掉。 + +**缺点**: + +1.id只有在定期填充时,会丢弃掉一些id,其他情况下,id是完全连续的。假如每次使用量比较大,大部分时候都是5分钟内能用掉50%的话,那么就就不会触发定期填充,也没有id丢弃,导致id会一直连续,容易泄露数据信息,所以最好自定义丢弃逻辑。 + +2.其次是id跟生成id时系统的时间戳无关了,可能无法满足一些特殊业务的需求。 + +### 自己的方案 + +我们自己的主要是根据uid-generator来做的,因为只是启动的时候依赖数据库,不需要引进新的依赖。做的优化主要是修改了位数分配,使得支持最大服务年限更长,30位时间差+16位机器号+7位序列号。做了时钟回拨优化,回拨差值较小时进行等待。以及做了序列号丢弃的优化。 + +### 谈一谈你对分布式事务的理解? + +#### 2PC方案(2阶段提交制) + +![图片](../static/640-8127488.) + +这种方案就是引入了一个协调者,主要分为prepare和commit两个阶段,在第一阶段其实就是协调者给各个业务系统发送命令,业务系统收到后,就会开始执行这个子事务的具体操作,然后给协调者反馈,子事务执行成功还是失败,发送后,业务系统就会同步阻塞等待协调者的第二阶段的指令。 + +协调者第一阶段发送指令会进入超时等待状态,等待各个业务系统返回子事务的执行结果。 + +**执行成功,提交** + +如果所有业务系统都执行成功了,并且协调者收到了反馈,那么就认为整个事务执行成功了,就会通知各个业务系统进行提交。 + +**执行失败,回滚** +如果超过时间还没有收到所有业务系统返回的结果,或者是有业务系统返回了执行失败的结果,那么协调者就认为执行失败了,通知各个业务系统进行回滚,如果此时网络出现阻塞,业务系统收不到通知,那么协调者就会一直重试发送回滚的指令。 + +**缺点** + +1.单点问题:就是一旦协调者挂掉,所有子系统都会进入阻塞等待状态。 + +2.数据不一致的问题:如果在第二阶段协调者给子系统发送commit指令时,发生了局部网络异常,就会导致接收到commit的指令的子系统提交事务,而没有接收到commit指令的子系统没有提交事务,导致数据不一致。 + +3.同步阻塞:由于子系统在执行阶段都是同步阻塞的,自身没有超时的机制,一旦与协调者之间的网络断开,只能一直阻塞等待,等待协调者的指令。 + +#### 3PC三段提交制 + +这种方案就是分为三个阶段,canCommit,preCommit,doCommit三个阶段。 + +第一阶段是询问阶段 + +协调者会给参与者发送canCommit指令,询问能否正常执行,如果满足执行的条件的话,参与者就会返回ACK。 + +第二阶段是预执行阶段 + +协调者会给参与者发送preCommit指令,让参与者执行事务,但是执行完成不提交,参与者执行成功后会给协调者发送ACK。 + +第三阶段就是提交阶段 + +协调者如果收到所有参与者给他返回执行成功的ACK,那么他就会给所有协调者发送doCommit指令,让参与者提交。如果在第二阶段有一个参与者执行失败,给协调返回执行失败的结果,那么在第三阶段,协调者就会给参与者发送Abort指令,让参与者回滚。 + +![图片](../static/640-8127813.png) + +**与2PC的区别:** + +1.3pc是一个非阻塞的协议,为参与者引入了超时机制,在第一阶段或者第二阶段,参与者等待协调者的指令超时了,会进行回滚,第三阶段等待协调者的指令超时了,会进行自动提交。而2pc协议,如果协调者一直没有给参与者发指令,导致超时,参与者会一直进行阻塞等待。 + +2.2pc协议里面,第二阶段协调者挂了,选举出新的协调者是不知道参与者执行的事务是提交事务还是回滚事务。3pc里面到了第三阶段那么一定是执行提交事务。 + +**3pc的问题:** + +第三阶段如果发送的是abort回滚指令,假设有些参与者由于网络原因没有收到指令,超时后会进行自行提交事务,那么也会导致数据不一致的问题。 + +https://honeypps.com/architect/introduction-of-distributed-transaction/ + +https://www.infoq.cn/article/2018/08/rocketmq-4.3-release + +#### TCC + +TCC分为Try预留阶段,Confirm确认阶段,Cancel撤销阶段三个阶段。 + +比如某个事务需要A,B,C三个业务系统各自执行一些操作,那么事务管理器会发送Try指令,会让A,B,C三个业务系统各自去申请执行操作所需的一些资源,冻结库存之类的。A,B,C预留资源成功了就会通知事务管理器Try阶段执行成功了。那么事务管理器就会发送Confirm指令给三个业务系统,告诉他们进入到Confirm阶段,让A,B,C业务系统各自执行自己真正的事务操作。如果三个业务系统都执行成功,那么事务管理器就认为执行成功,如果有一个失败那么事务管理器就认为执行失败了,会通知每个业务执行Cancel操作,进行回滚。 + +(万一某个服务的 Cancel 或者 Confirm 逻辑执行一直失败怎么办呢? + + Cancel 或者 Confirm 一直没成功,会不停的重试调用它的 Cancel 或者 Confirm 逻辑,务必要它成功。) + +TCC框架主要有ByteTCC,TCC-transaction,Himly。 + +缺点: + +1.侵入性太强,每个业务系统还需要写Cancel对应的数据回滚相关的逻辑代码。 + + + + + +![img](../static/v2-90179fa933c0a389ffa6ac04e244a58f_b.jpg) + +https://www.cnblogs.com/jajian/p/10014145.html + +##### 本地消息表法 + +就是上游服务先执行操作,将操作记录到数据库中的本地消息表,并且此时这条操作记录的status设置为0,也就是未通知成功。然后将操作记录封装成Kafka消息发送到消息队列,下游系统接受到,进行消费,然后消费成功后调用上游服务的接口,通知他消费成功了,上游系统将本地消息表中这条记录的status设置为1,代表通知成功。 + +并且上游系统会定时扫描本地消息表,将status为0的操作记录,封装成Kafka消息,发送到消息队列。 + +并且下游系统是通过消息中操作记录的主键id来防止不重复消费,保证幂等性的。就是消费消息时,发送操作记录的id已经在数据库中存在了,就代表之前已经处理过了,不处理这条消息了。 + +##### 可靠消息最终一致性方案 + +RocketMQ在4.3以后,增加了对分布式事务的支持,就是将事务的执行状态保存在RocketMQ中,由RocketMQ去负责将commit状态的消息推送给下游系统。 + +![img](../static/66b6ae1dec5b96084c3a6d29174a20e3.png) + +1.上游系统发送prepare消息到RocketMQ。 + +2.prepare消息发送到RocketMQ成功后,上游系统开始执行本地事务。 + +3.如果上游系统本地事务执行成功,会发送commit消息到RocketMQ,RocketMQ会将这个消息提交,推送给消费者(也就是下游系统)。如果上游系统本地事务执行失败,会发送rollback消息到RocketMQ,RocketMQ会将这个消息撤销,不推送给消费者。 + +4.如果一个prepare消息一直没有接受到上游系统的commit或者rollback指令,这样就判定prepare消息超时了,RocketMQ会去查询上游系统的这个事务的执行状态,是成功了,还是失败,做下一步的处理。 + +**底层实现原理** + +RocketMQ使用了Half topic队列来保存所有prepare消息,使用Operation Topic队列来保存commit消息和rollback消息。这样通过Operation Topic就知道哪些消息commit了,可以推送给消费者,哪些消息rollback了,不需要推送给消费者。以及那些在Half topic中有,在Operation Topic中没有的消息,就是事务超时的消息。 + +https://www.infoq.cn/article/2018/08/rocketmq-4.3-release + +##### 最大努力通知方案 + +业务系统 A 执行本地事务完成后,发送个消息到 MQ,有一个个专门消费 MQ 的服务,来消费MQ的消息,消费完会在数据库中记录下来(或者放入到内存队列),之后就一直调用系统 B 的接口,要是系统 B 执行成功就提交,执行失败或者调用超时就一直重试,直到业务系统B执行成功。 + +### 如何设计秒杀系统? + +1.前端页面 +提前把静态资源部署到CDN,减少静态资源访问的压力,其次是可以为静态资源的header设置成强缓存cache-control,2分钟后才过期,这样当第一次请求完静态资源后,再次刷新时,就会使用浏览器里面的缓存的静态资源,而不是再发请求。 +2.nginx层面 +使用limit_req_zone模块针对用户id为key,进行限流,放在同一个用户恶意发起多个请求,限制每个用户每5分钟只能请求100次接口。 +3.业务系统设置限流熔断 +当业务系统收到的请求达到一定限制后,停止接受请求,可以使用hystrix进行限流。如果是秒杀商品,当Redis里面存的商品库存减完时,就对所有用户返回统一的状态码,告诉前端商品被抢光了,不再进行后面的业务逻辑。 +4.使用消息队列削峰 +就是如果秒杀请求对应的业务逻辑处理时间如果过长,例如减库存,生成订单等,业务系统可以先将用户的秒杀请求封装成Kafka消息,发送到消息队列,由其他业务系统消费Kafka消息,完成生成订单等耗时操作。 +https://www.zhihu.com/question/54895548/answer/1352510403 \ No newline at end of file diff --git a/docs/ZooKeeper.md b/docs/ZooKeeper.md new file mode 100644 index 0000000..b916bc3 --- /dev/null +++ b/docs/ZooKeeper.md @@ -0,0 +1,100 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +#### [1.Zookeeper是怎么进行选举的?](#zookeeper是怎么进行选举的?) + +### Zookeeper是怎么进行选举的? + +##### 节点的状态 + +looking 正在参与竞选状态,会参与投票 +leading 选举结束,主节点的状态 +following 选举结束,从节点的状态 +一开始所有节点都是looking状态,进行选举,选举出主节点后, +主节点的状态是leading,其他节点状态是following,主节点挂了之后,需要进行新一轮的选举,所有节点又变成looking。 + + +所有节点都有两个属性,SID:节点ID,zoo.cfg中配置的myid,ZXID:节点当前的最大事务ID +选举的目的就是选目前所有节点中拥有最大ZXID的节点作为Leader,如果拥有的ZXID相同,就选取SID最大的节点作为Leader。 + +##### 全新的集群leader选举 +启动时,当集群没有leader时,每个机器会将票投给SID最大的机器(越晚加入的机器SID越大) +有leader时会将票投给leader。 +假设有五个节点,一开始A启动时之会将票投给自己,B启动时之后将票投给B,A也投给B,C启动时,A,B,C都投个C +C得票超过半数,C成为leader,之后加入的D,E也只会投给C。 + +#### 非全新的集群leader选举 +##### 基本原则是 +1.选epoch值大的,epoch值小的投票结果会被忽略掉。 +2.epoch值相同时,把票投给ZXID大的节点。 +3.ZXID相同时,把票投给SID(server id)大的节点。 +得票超过半数的节点会成为leader。 +通常是数据越新的节点越有可能成为主节点。 + +##### 投票阶段 + +##### 1.初始化选票 +将逻辑时钟+1,初始化选票, +每个节点都会投给自己,然后选票发给其他节点 +##### 2.处理选票信息 +从其他节点B收到投票信息后,进行处理 + +2.1 如果本节点的逻辑时钟小于接受这条投票的逻辑时钟, + +说明本节点之前错过了上一轮的投票,将当前存储的选票信息清空, + +2.2 如果本节点的逻辑时钟大于接受的这条投票的逻辑时钟,那么忽略掉这条投票信息。 + +2.3 本节点的逻辑时钟等于接受的这条投票的逻辑时钟,那么进行处理,与本节点当前投票的结果进行比较 +先比较ZXID(数据ID,越大代表数据越新),ZXID越大的应该当leader,ZXID相同比较SID,SID越大的当leader。 + +2.4如果比较的结果跟当前节点的投票结果不一致,那么需要更改选票,将更改后的选票结果发送给其他节点。 + +2.5 将其他节点B的投票结果记录下来 + +##### 3.统计选票 + +对当前收到的所有投票信息进行统计,看是否有节点获得半数以上的选票,有的话就将它设置为leader,然后终止投票,否则继续投票,继续步骤2。 + +##### 4.发现阶段 + +所有follower会向leader发送epoch和ZXID(最大事务ID),然后leader选取最大的事务ID作为当前最新的ID +##### 5.同步阶段 + +通知其他follower节点进行同步,将数据更新到最大的事务ID。 +##### 原子广播阶段 +这时正式向客户端提供服务,leader接受客户端的写请求后,会将请求通过队列发送个每个节点,每个节点收到消息后将记录写到磁盘, +并且返回ACK给leader,当半数以上的从节点返回ACK后,leader才commit这条更新。 + +https://www.ymq.io/2018/05/23/zookeeper-election/ + +https://blog.csdn.net/MuErHuoXu/article/details/85864535 + +https://blog.csdn.net/hotchange/article/details/81192122 + +### Zookeeper工作流程? + +首先客户端连接zookeeper集群中的任何一个节点,可以是leader节点,也可以是follower节点,一旦连接,节点会给客户端分配会话ID,并向客户端发送确认,如果客户端收到确认,那么连接成功,客户端会有规律地给zookeeper发送心跳包,确保连接没有断开。 + +* 客户端向zookeeper从节点发送读请求,节点会直接从数据库中找到这个节点的数据然后返回。 + +* 客户端向zookeeper从节点发送写请求,节点会将znode路径和数据转发到leader节点,leader会将写请求转换为proposal提案,并且分配一个事务ID zxid,将这个proposal放到每个节点的队列(主节点会给每个从节点分配一个专用队列)中去,然后会根据先进先出的策略,将消息发送给从节点,从节点接收到后会将事务写入到磁盘中去,然后返回ACK响应给主节点,当主节点接收到半数以上的从节点的ACK响应后,主节点会认为这个事务提交成功,完成这个事务提交,同时给所有从节点发送commit消息,从节点接收到消息后,会将这条事务提交。 + +由此看来zookeeper没法保证客户端读取的都是最新的数据, + +### 分布式 + +分布式最大的难点在于各个节点的状态怎么同步。 + +CAP理论 + +C是一致性,Consistency,就是各个节点保存的数据都是最新的。 + +A是Availability, 可用性,每次请求都可以获得响应,但是没法保证是最新的响应。 + +P是分区容错性(Partition tolerance),就是每个节点可能都存在于不同的子网络,也就是不同的分区,不同分区之间的通信总是有可能发生故障的,或者总是有可能存在一些节点挂了的情况。 + +可以认为P总是成立的,C和A是无法同时做到。所以一般对于数据一致性要求特别高的业务,例如支付,交易相关的业务,就是会优先保证一致性C和分区容错性P,就是保证数据一致性,例如让所有子节点都收到更新后才算提交成功,就像MySQL主从同步中的全同步模式一样。普通的业务是优先保证可用性A和分区容错性P,比如在MySQL主从同步时,默认就是异步的方式,我们执行一条更新SQL,只需要主节点更新成功就行就对事务进行提交,不需要等待从节点更新数据成功,主节点会异步把SQL发送给从节点。 + +### 分布式锁 + +https://mp.weixin.qq.com/s/32lWC4PA7nF13_2wRo6i3Q \ No newline at end of file diff --git a/docs/_sidebar.md b/docs/_sidebar.md new file mode 100644 index 0000000..bd331c5 --- /dev/null +++ b/docs/_sidebar.md @@ -0,0 +1,35 @@ +- [首页](README.md) +* Java + - [基础](docs/JavaBasic.md) + * 容器 + - [ArrayList和LinkedList](docs/ArrayList.md) + - [HashMap和ConcurrentHashMap](docs/HashMap.md) + - [多线程](docs/JavaMultiThread.md) + - [锁相关](docs/Lock.md) +* Redis + - [基础](docs/RedisBasic.md) + - [数据结构](docs/RedisDataStruct.md) + - [持久化(AOF和RDB)](docs/RedisStore.md) + - [高可用(主从切换和哨兵机制)](docs/RedisUserful.md) +* MySQL + - [基础](docs/MySQLNote.md) + - [慢查询优化实践](docs/MySQLWork.md) +* JVM + - [基础](docs/JavaJVM.md) +- [Kafka](docs/Kafka.md) +- [ZooKeeper](docs/ZooKeeper.md) +- [HTTP](docs/HTTP.md) +- [Spring](docs/Spring.md) +- [Nginx](docs/Nginx.md) +- [系统设计](docs/SystemDesign.md) +* 算法 + - [《剑指Offer》解题思考](docs/CodingInterviews.md) + - [《LeetCode热门100题》解题思考(上)](docs/LeetCode.md) + - [《LeetCode热门100题》解题思考(下)](docs/LeetCode1.md) +- [大厂面试公众号文章系列](docs/BATInterview.md) +* 读书笔记 + - [《Redis设计与实现》读书笔记 上](docs/RedisBook1.md) + - [《Redis设计与实现》读书笔记 下](docs/RedisBook2.md) + - [《MySQL必知必会》读书笔记](docs/MySQLBook1.md) + - [《深入理解Java虚拟机-第三版》读书笔记](docs/JVMBook.md) +- [好书推荐](docs/bookRecommend.md) diff --git a/docs/algorithm.md b/docs/algorithm.md new file mode 100644 index 0000000..eff4a06 --- /dev/null +++ b/docs/algorithm.md @@ -0,0 +1,1088 @@ +高频算法面试题集合 + +#### [1.常见的排序算法](#常见的排序算法) + +#### [2.二分查找](#二分查找) + +#### [3.查找链表倒数第K个节点](#查找链表倒数第K个节点) + +#### [4.链表反转](#链表反转) + +#### [5.查找第K大的数](#查找第K大的数) + +#### [6.有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M,返回频数最高的100个词](#有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M,返回频数最高的100个词) + +#### [7.9个硬币中有一个劣币,用天平秤,最坏几次?](#9个硬币中有一个劣币,用天平秤,最坏几次?) + +#### [8.编辑距离](#编辑距离) + +#### [9.打印杨辉三角](#打印杨辉三角) + +#### [10.LRU算法](#LRU算法) + +#### [11.【面试算法题】阿拉伯数字转化为中文读法](#[面试算法题]阿拉伯数字转化为中文读法) + +### 常见的排序算法 + +![img](../static/640-2913012.png) +![img](../static/640-2921069.jpeg) + +## 冒泡排序 + +对从[0,length-1]范围内的元素进行比较和交换,将大的元素交换到后面去,一次遍历后就可以将最大的元素交换至末尾,然后下次遍历范围将减1,在[0,length-2]范围内进行,一直到最后。 + +#### 优化 + +使用冒泡排序的过程中,如果有一趟冒泡过程中元素之间没有发生交换,那么就说明已经排序好了,可以直接退出不再继续执行后续的冒泡操作了。 + +```java +int[] sorted(int[] array) { + if (array == null || array.length ==0 || array.length ==1) {return array;} + for(int i = array.length;i>0;i--) {//i代表遍历的最大范围 + int sortedFlag = 0; + for(int j = 1;jarray[j]) {//进行比较,将大的元素交换到后面去 + int temp = array[j]; + array[j] = array[j-1]; + array[j-1]=temp; + sortedFlag=1; + } + } + // 该趟排序中没有发生,表示已经有序 + if (0 == sortedFlag) { + break; + } + } + return array; +} +``` + +## 插入排序 + +就是每次从未排好序的序列中,每次取出一个元素,插入到已排好序的序列中去。比较好的写法就是数组前面一段是排好序的,这一段往前面进行比较然后互换。 + +```java +int[] sorted2(int[] array) { + if(array == null || array.length==0 || array.length==1) {return array;} + for(int i=1;i0;j--) { + if(array[j]=end) { return; } + //进行分组,一直分到只有两个元素,在小组内进行归并 + int middle = start + (end - start)/2; + mergeSort(array,start,middle,tempArray); + mergeSort(array,middle+1,end,tempArray); + + //进行合并 + int i = start; + int j = middle+1; + int currentIndex = start; + //进行合并,每次取最小值 + while (i <= middle && j<= end) { + tempArray[currentIndex++] = array[i] < array[j] ? array[i++] : array[j++]; + } + //对剩余元素进行归并 + while (i <= middle) { + tempArray[currentIndex++] = array[i++]; + } + //对剩余元素进行归并 + while (j <= end) { + tempArray[currentIndex++] = array[j++]; + } + //将排序好的数组拷贝回原数组 + for (int k = start; k <= end; k++) { + array[k] = tempArray[k]; + } +} +``` + +### 快速排序 + +先取第一个元素作为中间值,小于中间值的元素放一组,大于中间值的元素放一组,然后继续对每一组进行递归排序,最终让数组有序。 + +```java +int[] quickSorted(int [] array, int start,int end) { +//如果start和end相等就直接结束循环 + if(start>=end) {return array;} +//取第一个元素作为基准值 + int base = array[start]; + int i = start;//左边从第一个元素开始,如果取得基准值正好是最小值,然后遍历从第二个开始,会导致第二个位置的值与基准值交换,而第二个位置的值是>基准值的 + int j = end;//右边从最后一个元素开始 + //只要i base && i= 0 ; i--) { + adjustHeap(array,i,array.length); + } + //2.大顶堆建立完毕后,堆顶就是最大值,交换到数组末尾 + for (int i = array.length-1; i > 0 ; i--) { + //每次调整完毕后堆顶都是最大值 + swap(array,0,i); + //对0到i-1范围内的元素看成一个堆,进行大顶堆调整 + adjustHeap(array,0,i); + } + return array; + } + //调整大顶堆,需要调整节点i, + //使节点i与它的子节点都满足大顶堆的性质(就是父节点>左节点和右节点) + void adjustHeap(int[] array,int i,int length) { + //循环结束的条数是节点i存在左节点 + while (2*i+1 array[left] ? i : left; + if (right array[max]) {//右节点存在,并且还比最大值大 + max = right; + } + if (max == i) {//当前已经是最大值 + break; + } else {//左节点或者右节点是最大值,那么就将当前节点与最大的节点交换 + swap(array,i,max); + i = max; + } + } + } + //交换元素 + void swap(int[] array,int a,int b){ + int temp = array[a]; + array[a] = array[b]; + array[b] = temp; + } +``` + +### 桶排序 + +核心思想就是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排序完成之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了。一般步骤是: + +- 先遍历一边序列,获得最大值和最小值,确定要排序的数据的范围,然后分为n个范围; +- 然后根据范围将数据分到桶中(可以选择桶的数量固定,也可以选择桶的大小固定); +- 之后对每个桶进行排序; +- 之后将桶中的数据进行合并; + +桶排序适合应用在外部排序(数据存储在磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中。)中。比如要排序的数据有 10 GB 的订单数据,但是内存只有几百 MB,无法一次性把 10GB 的数据全都加载到内存中。这个时候,就可以先扫描 10GB 的订单数据,然后确定一下订单数据的所处的范围。比如订单的范围位于 1~10 万元之间,那么可以将所有的数据划分到 100 个桶里。再依次扫描 10GB 的订单数据,把 1~1000 元之内的订单存放到第一个桶中,1001~2000 元之内的订单数据存放到第二个桶中,每个桶对应一个文件,文件的命名按照金额范围的大小顺序编号如 00、01,即第一个桶的数据输出到文件 00 中。 + +理想情况下,如果订单数据是均匀分布的话。但是,订单数据不一定是均匀分布的。划分之后可能还会存在比较大的文件,那就继续划分。比如订单金额在 1~1000 元之间的比较多,那就将这个区间继续划分为 10 个小区间,1~100、101~200 等等。如果划分之后还是很大,那么继续划分,直到所有的文件都能读入内存。 + +### 二分查找 + +查找数组中是否存在某元素的算法 + +```java +int findByHalf(int[] array, int target) { + if (array == null || array.length==0) { + return -1;//代表没有合适的元素 + } + int left = 0; + int right = array.length-1; + while(left<=right) {//需要注意是小于等于right,否则查找范围会是[left,right),会漏掉对最后一个元素的比较 + int middle = left + (right - left)/2; + if(array[middle]target) { + right = middle-1; + } else { + return middle; + } + } + return -1;//代表没有合适的元素 +} +``` + +二分查找左边界算法 + +左边界的定义是第一个>=目标值的下标,如果所有元素都<目标值,那么返回的会是最后一个元素的下标+1 +```java +//1,2,3,4,5,6,7,8,9 +int find_left_bound(int[] array,double target) { + if(array == null|| array.length==0) {return -1;} + int left = 0; + int right = array.length-1; + while(left<=right) { + int middle = left+(right-left)/2; + if(array[middle]==target) { + right = middle-1; + } else if (array[middle] > target) { + right = middle-1; + } else if (array[middle] < target) { + left = middle + 1; + } + } + if (left >= nums.length)//如果所有元素都比target小,left会等于nums.length,此时是越界的 + {return -1;} + return left; +} +``` + +寻找左边界的二分查找算法 + +```java +int findLeft(int[] array,int target) { + if (array==null|| array.length==0) { + return -1; + } + int left = 0; + int right = array.length-1; + while (left<=right) { + int mid = (left+right)/2; + if (array[mid] == target) { + if (mid==0 || array[mid-1]K,说明第K大的数在左边,继续从左边的子序列中找,index=end) { + return null; + } + + int base = array[start]; + int i = start; + int j = end; + while (i= base && iK) {//说明数在左边的区间 + return findK(array, start, i-1, K); + } else {//说明数在右边的区间 + return findK(array,i+1,end, K); + } + } +``` + +#### 堆排写法 + +PriorityQueue是一个优先级队列,可以认为是一个小顶堆,每次poll元素出来都是poll出最小的元素,所以queue中包含K个元素,每次遍历时将元素添加到queue中,并且将最小的元素poll出。 + +```java +private static Integer findKWayTwo(int array[], int K) { + PriorityQueue queue = new PriorityQueue(); + for (int i = 0; i < array.length; i++) { + queue.add(array[i]); + if (queue.size()>K) { + queue.poll(); + } + } + return queue.peek(); +} +``` + +#### 插入排序解法 + +就是一个长度为K的排序好的序列,每次遍历时拿元素A与这个序列里面的最小元素B进行比较,A>B,就将A往排序好的序列里面插入。 + +```java +public static Integer findKByPickSort(int[] input, int k) { + ArrayList arrayList = new ArrayList(); + if(input==null || input.length==0 ||input.length arrayList.get(arrayList.size()-1)) {//子数组个数达到了K,并且当前数比子数组最后一个数小 + arrayList.remove(arrayList.size()-1); + arrayList.add(input[i]); + } else { + continue; + } + //将最后一个元素移动合适的位置 + for (int j = arrayList.size()-1; j > 0 ; j--) { + if (arrayList.get(j) > arrayList.get(j-1)) { + int temp = arrayList.get(j); + arrayList.set(j, arrayList.get(j-1)); + arrayList.set(j-1, temp); + } + } + } + return arrayList.get(k-1); + } +``` + +### 01背包问题 +就是有很多个物品,每个物品有一个价值属性,一个重量属性,假设背包总容量为capcity,选择哪些物品,可以使得最终装的物品价值最大。 +有两种节点: +##### 分治法 +就是假设当前有i个物品,我们对于第i个物品进行选择,只有两种可能 +要么选择物品i,那么背包的容量就变为capcity-weight[i],然后继续对剩下的i-1的这些物品进行选择: + +总价值f(i,capacity) = value[i] +f(i-1,capcity-weight[i]) + +要么不选择物品i,容量还是capcity,继续对剩下的i-1的物品进行选择: + +总价值f(i,capacity) = f(i-1,capcity) + +```java + int testKnapsack1(int[] value,int[] weight, int i, int capacity) { + int result = 0; + if (i < 0 || capacity == 0){ + // 已经选择到最后了 + result = 0; + } else if(weight[i] > capacity) { + // 装不下该珠宝 + result = testKnapsack1(value,weight,i-1, capacity); + } else if (weight[i] <= capacity) {// 可以装下 + //选择物品i + int choose = testKnapsack1(value,weight,i-1, capacity-weight[i]) + value[i]; + //不选择物品i + int notChoose = testKnapsack1(value,weight,i-1, capacity); + result = choose > notChoose ? choose : notChoose; + } + return result; + } +``` +##### 动态规划解法 +```java + //动态规划解法 + public int testKnapsack2(int[] value,int[] weight, int capacity) { + int[][] dp = new int[weight.length][capacity+1]; + //对前i个物品进行选择 + for (int i = 0; i < weight.length; i++) { + //每次背包总重量为j + for (int j = 1; j <= capacity; j++) { + if (i==0) {//当前只有一个物品 + dp[i][j] = weight[i] > capacity ? 0 : value[i]; + } else if (j < weight[i]) {//背包装不下,那就不装这个物品 + dp[i][j] = dp[i-1][j]; + } else {//当前背包装得下 + //不装此物品 + int notChoose = dp[i-1][j]; + //装此物品 + int choose = dp[i-1][j-weight[i]] + value[i]; + dp[i][j] = notChoose > choose ? notChoose : choose; + } + } + } + return dp[weight.length-1][capacity]; + } +``` + + + +### 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M,返回频数最高的100个词 + +首先这个问题的难点在于内存太小,1M/16 byte =2的16次方,也就是在极端情况下,单词都是16字节时,内存中可能最多存储2的16次方个。 + +执行流程如下: +先hash分片->每个小文件堆排取前100->对每个小文件的前100做归并排序。 + +1.由于内存限制是1M,所以如果每个小文件小于1M以下才能全部加载到内存中,所以可以设置模为5000,遍历每个单词,对单词取hash值%5000,得到index,写入到index名的小文件下,理论上这样的是可以达到每个小文件都是小于1M的,如果大于1M,需要对文件继续进行hash运算,切分成小文件。 + +2.遍历每个小文件,每个小文件理论上都是小于1M的,可以直接加载到内存中,使用堆排的方法对小文件进行排序,记录每个单词的单词名和出现次数,写入一个新的记录次数的小文件。 + +3.然后进行归并排序取一个记录次数的文件,建了一个包含100个单词的小顶堆,遍历一次所有记录次数文件,每次遍历时取小文件的前100,与堆顶进行比较,如果比堆顶大就替换堆顶,并且对小顶堆进行调整,将最小的元素调整到堆顶。 + +对于多次hash后的文件还是大于内存限制应该怎么办呢? +这个时候就只能先对文件按照顺序进行切分,切分成小文件,然后加载每个小文件,通过map来记录每个单词及出现次数,然后根据map按照key的顺序生成一个记录了单词和出现次数的map文件。对这些map文件进行合并,每次取两个map合并后生成一个map,最终得到一个map文件,使用堆排遍历map中的key,得到前100个词,作为这部分hash文件的前100。 + +### 全排序问题 + +就是给定一个字符串,返回所有可能的排序结果。例如aba的排序结果有aba,aab,baa。可以认为每一个字符串的排序结果是等于每个非重复字母出现在第一个index+后面子序列每个的组合。 + +```java +//用于收集每种序列 +ArrayList list = new ArrayList(); +public void PermutationHelper(char[] charArray,int start) { + if (charArray == null || charArray.length == 0) { + return; + } + if(start == charArray.length-1) {//最后一个元素 + list.add(String.valueOf(charArray)); + return; + } + HashSet set = new HashSet(); + for(int i = start;i set = new HashSet(); + while (quick maxLength ? quick - slow +1 : maxLength; + quick++; + } + } + return maxLength; +} +``` + +### 9个硬币中有一个劣币,用天平秤,最坏几次? + +最坏是3次可以称出来。 + +**第一次称**,123 VS 456, + +* 如果平衡,那么说明剩下的789里面有劣币, + 然后**第二次称**7VS8,平衡,9就是劣币,否则7和8中有一个是劣币,那么**第三次称**拿真币1和7来称,如果平衡则8是假币,不相等则7是假币。 +* 如果不平衡,说明现在的123,456个币中有一个是劣质币 + 假设 + 重的那一组是1 2 3 + 轻的那一组 4 5 6 + 由于**假币的所在的组要么会一直重(如果假币比真币重),要么会一直轻(如果假币比真币轻),所以如果一会在重组一会在轻组出现的肯定是真币**。 + 所以我们先去掉3和6,并且将2和5进行调换,也就是**第二次称**对15和42进行称。 + 1 5 + 4 2 +* 假设天平平了,说明15和42都是真币,3和5才假币,**第三次称**拿一个其他的币与3称,相同则5是假币,不相等则3是假币。 +* 假设天平没有平,并且1 5是重的那一方,由于不平衡,所以排除3和6,由于在之前4 5 6是轻的一方,现在1 5又是重的一方,所以排除5,5不会是假币,同理也排除2,所以只有1和4有可能是假币,**第三次称**拿一个其他的币与1称,平衡则4是假币,不相等则1是假币。 +* 假设天平没有平,并且1 5是轻的那一方,由于不平衡,所以排除3和6,同理根据上面的原理,可以排除一会在重组一会在轻组的1,4,所以2和5有可能是假币。同理**第三次称**可以得出2和5谁是假币 + +#### 如果是12个硬币,里面有一个假币呢 + +分成四组,**第一次称**1234 VS 5678 + +1.平衡说明假币在9 10 11 12中,**第二次称**9 10 VS 真币1 真币2, + +* 平衡代表11,12中存在假币,**第三次称**11 VS 真币1,平衡代表12是假币,否则11是假币。 +* 同理不平衡,说明9 10存在假币,对9和10实时上面的称法。 + +2.不平衡,说明假币存在于1到8中, +假设 +重组是1 2 3 4 +轻组是5 6 7 8 + +加一个真币X,好分成三组,为了提高区分度, +第一组 1 2 5(两个重组元素+一个轻组元素) +第二组 3 6 X(一个轻组元素+一个重组元素+一个真币) +**第二次称**1 2 5 VS 3 6 X + +* 假设平衡,说明4,7,8中存在假币,称一下7和8,平衡,那么4是假币,不平衡,由于一会在重组的,一会在轻组的肯定是真币,而7,8之前都在轻组,所以7和8对称时,轻的是假币。 +* 假设1 2 5 重,由于一会在重组的,一会在轻组的肯定是真币,根据这个原则,5,3肯定是真币,所以只有1,2,6存在可能。同理,根据上面的方法**第三次称**找出假币 +* 话说1 2 5轻,由于一会在重组的,一会在轻组的肯定是真币,根据这个原则,可以排除1,2,它们是真币。只有3和5存在可能,**第三次称**找出假币。 + +### 编辑距离 + +递归解法 + +```java +// 假设是要将a变成b的样子 +static int findMinValue(char[] a, int aLength, char[] b, int bLength) { + if (a == null || aLength <= 0) { return bLength;} + if (b == null || bLength <= 0) { return aLength; } + if (a[aLength - 1] == b[bLength - 1]) {//相等就前移 + return findMinValue(a, aLength - 1, b, bLength - 1); + } else { + //对字符串a的末尾插入当前b末尾的字符,相当于是b可以进行前移 + int first = findMinValue(a, aLength, b, bLength - 1) + 1; + //对字符串a的末尾字符进行删除,相当于a可以进行前移 + int second = findMinValue(a, aLength - 1, b, bLength) + 1; + //对字符串a的最后一个字符进行替换,相当于a,b都前移 + int three = findMinValue(a, aLength - 1, b, bLength - 1) + 1; + int min = first < second ? first : second; + return min < three ? min : three; + } +} +``` + +动态规划解法 + +```java +static int findMinValueInNewWay(char[] a, int aLength, char[] b, int bLength) { + int [][] dp = new int[aLength][bLength]; + //这里就是假设b字符串为空时,a的每个子串的编辑距离,也就等于每个子串的长度。 + for (int i = 0; i < aLength; i++) { dp[i][0] = i+1; } + //同理,这里是假设a字符串为空 + for (int j = 0; j < aLength; j++) { dp[0][j] = j+1;} + //计算a,b每个子串的之间的编辑距离 + for (int i = 1; i < aLength; i++) { + for (int j = 1; j < bLength; j++) { + if (a[i] == b[j]) { + dp[i][j] = dp[i-1][j-1]; + } else { + //替换 对a当前遍历下标的值进行替换,相当于a,b各减掉一个字符 + int first = dp[i-1][j-1]+1; + //查 a进行插入b当前下标的字符,相当于b减掉一个字符 + int second = dp[i][j-1] +1; + //a删除一个元素 + int three = dp[i-1][j] +1; + int min = first < second ? first : second; + dp[i][j] = min < three ? min : three; + } + } + } + return dp[aLength-1][bLength-1]; +} +``` + +### 打印杨辉三角 + +思路其实是使用一个数组来存储上一层节点的值,然后根据每个节点index去数组中可以取值,父左节点的下标等于index-1,父右节点的下标等于index。 + +```java +static void printYanghui(int rows) { + int[] a = new int[rows]; + a[0] = 1; + //i代表第几层 + for (int i = 0; i < rows; i++) { + int left = 0; + int right; + //j代表这个元素在这一层是第几个,也就是元素在当前层数的序号 + for (int j = 0; j <= i; j++) { + //每个元素右边的父节点的序号其实跟这个元素的序号是一样的 + right = a[j]; + //每个节点的值是等于父左节点+父右节点 + a[j] = left + right; + System.out.print(" " +a[j]); + //当前父右节点其实是下一个元素的父左节点,由于上面已经对a[j]进行赋新值, + // 所以需要使用left来存 + left = right; + } + System.out.println(" "); + } +} +``` + +#### 数学解法 + +根据一个数学规律来计算,就是每一层一开始是数都是1,假设前一个数是a,后面的数b,满足b = a*(上一层的层数-a的下标)/(a的下标+1) + +```java +static void test2(int rows) { + for (int i = 0; i < rows; i++) { + int index = 1; + for (int j = 0; j <= i; j++) { + System.out.print(" " + index); + index = index * (i - j)/(j+1); + } + System.out.println(" "); + } +} +``` + +## LRU算法 + +LRU其实就是Last Recent Used,就是最近使用淘汰策略,所以当空间满了时,就根据最近使用时间来删除。一般是使用一个双向链表来实现,同时为了快速访问节点,会使用一个HashMap来存储键值映射关系。(需要注意的是,为了在内存满时删除最后一个节点时,可以以O(1)时间复杂度从HashMap中删掉这个键值对,每个节点除了存储value以外,还需要存储key)。 + +#### 添加新元素的过程: + +首先我们增加两个方法,remove()方法用于删除一个节点,addNewNodeToHead()代表添加一个节点到头部。 + +1.判断节点是否已经存在于链表中,是的话,找出节点, + +1.1更新value, + +1.2调用remove()方法删除节点, + +1.3调用addNewNodeToHead()将节点添加到链表头部。 + +2.节点不存在于链表中,那么判断链表长度是否超出限制, + +2.1是的话remove(lastNode.key) + +2.2创建一个新节点,调用addNewNodeToHead()将节点添加到链表头部。 + +remove()方法的细节,主要是更新node的前后节点的next或pre指针,以及更新后需要判断删除的节点是否是headNode或者lastNode,是的话同时需要更新headNode或者lastNode。 + +addNewNodeToHead()方法细节,主要是要先判断head是否为null,是的话说明链表为空,需要将headNode和lastNode都设置为node,不为null就执行添加操作,将headNode.pre设置为node,node的next设置为headNode,headNode=node; + +```java +//双向链表 +public static class ListNode { + String key; + Integer value; + ListNode pre = null; + ListNode next = null; + ListNode(String key, Integer value) { + this.key = key; + this.value = value; + } +} +ListNode headNode; +ListNode lastNode; +int limit=4; +HashMap hashMap = new HashMap(); +public void put(String key, Integer val) { + ListNode existNode = hashMap.get(key); + if (existNode!=null) {//有老的节点,只是更新值,先从链表移除,然后从头部添加 + existNode.value=val; + remove(key); + addNewNodeToHead(existNode); + } else { + //达到限制,先删除尾节点 + if (hashMap.size() == limit) { remove(lastNode.key); } + ListNode newNode = new ListNode(key,val); + addNewNodeToHead(newNode); + } +} +public ListNode get(String key) { + + ListNode node = hashMap.get(key); + if(node == null) { + return null; + } + remove(node.key); + addNewNodeToHead(node); + return node; +} +public void remove(String key) { + ListNode deleteNode = hashMap.get(key); + hashMap.remove(key); + ListNode preNode = deleteNode.pre; + ListNode nextNode = deleteNode.next; + //删除操作需要更新pre节点的next指针和next节点的pre指针,以及更新head和last + if (preNode!=null) { preNode.next = nextNode; } + if (nextNode!=null) { nextNode.pre = preNode; } + if (headNode == deleteNode) { headNode = nextNode; } + if (lastNode == deleteNode) { lastNode = preNode; } +} +private void addNewNodeToHead(ListNode node) { + hashMap.put(node.key,node); + if (headNode==null||lastNode==null) { + headNode = node; + lastNode = node; + return; + } + headNode.pre = node; + node.next = headNode; + headNode = node; +} +``` + +##### 使用LinkedHashMap实现的算法 + +使用LinkedHashMap实现LRU算法, + +* LinkedHashMap默认的accessOrder为false,也就是会按照插入顺序排序, + 所以在插入新的键值对时,总是添加在队列尾部, + 如果是访问已存在的键值对,或者是put操作的键值对已存在,那么需要将键值对先移除再添加。 +* 如果是将accessOrder设置为true,get已有键值对时就不需要删除key了,会自动调整顺序,put方法需要在添加或者更新键值对后调用LinkedHashMap#get()访问key,调整顺序。 + +```java +//accessOrder为false,按照插入顺序排序的写法 +public static class LRUCache { + int capacity; + Map map; + public LRUCache(int capacity) { + this.capacity = capacity; + map = new LinkedHashMap<>(); + } + public int get(int key) { + if (!map.containsKey(key)) { + return -1; + } + //先删除旧的位置,再放入新位置 + Integer value = map.remove(key); + map.put(key, value); + return value; + } + + public void put(int key, int value) { + if (map.containsKey(key)) { + map.remove(key); + map.put(key, value); + return; + } + //超出capacity,删除最久没用的,利用迭代器,删除第一个 + if (map.size() >= capacity) { + map.remove(map.keySet().iterator().next()); + } + map.put(key, value); + } +} +``` + +accessOrder为true,按照访问顺序排序的实现方法 + +```java +public static class LRUCache2 { + int capacity; + LinkedHashMap linkedHashMap; + LRUCache2(int capacity) { + this.capacity = capacity; + //如果要修改accessOrder只能使用这种构造器方法来创建LinkedHashMap + linkedHashMap = new LinkedHashMap(16,0.75f,true); + } + public int get(int key) { + Integer value = linkedHashMap.get(key); + return value == null ? -1 : value; + } + public void put(int key, int val) { + linkedHashMap.put(key, val); + if (linkedHashMap.size() > capacity) { + linkedHashMap.remove(linkedHashMap.keySet().iterator().next()); + } + } + //通过调用get()方法访问key来调整顺序 + linkedHashMap.get(key); +} +``` + +### 【面试算法题】阿拉伯数字转化为中文读法 + +例如我们要将10100转换为中文,总体流程就是先拿10100/1个亿,发现结果为0,说明不会包含亿这个数量级,然后10100/1万,得到结果result为1,余数remain为100,说明包含万这个数量级,我们的结果肯定是等于 "result的中文表示"+单位"万"+"余数的中文表示",所以就对问题进行了分解,f(n) = f(n/数量级)+数量级单位+f(n%数量级) + +```java + static String[] nameArray1 = {"","一","二","三","四","五","六","七","八","九"}; + static String[] nameArray2 = {"","十","百","千","万","亿"}; + static int[] intArray = {1,10,100,1000,10000,100000000}; + + public static String numToChinese(int num) { + for (int i = intArray.length-1; i >= 0; i--) { + int part1 = num/intArray[i]; + int part2 = num%intArray[i]; + if (i==0) {//到个位了 + return nameArray1[part1]; + } + if (part1>0) { + //整除部分,例如10100,整除部分就是十 + String left = numToChinese(part1); + //整除部分的单位,例如10100,整除部分的单位就是万 + String unitString = nameArray2[i]; + //余数部分,例如10100,余数部分就是一百 + String right = numToChinese(part2); + return left + unitString + right; + } + } + return ""; + } +``` +## 122.买卖股票的最佳时机 II +给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 + +设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 + +注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 + +示例 1:  +```java +输入: [7,1,5,3,6,4] +输出: 7 +解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 +  随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 +``` + +##### 解题思路 + +按照状态来分,每天只有持有股票和不持有股票两种状态,我们使用 +一个二维数组dp[i][isHoldStock]来保存当天利润最大值, +i代日期,isHoldStock代表当天是否持有股票, 1代表当天持有了股票 + +如果第i天持有股票,那么要么是之前买的,要么是今天买的 +dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i]) +如果第i天未持有股票,那么要么是前一天也没有持有股票,要么是今天把股票卖了 +dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]) + +初始状态第一天已持有股票 dp[0][1] = -prices[0] +初始状态第一天未持有股票 dp[0][0] = 0 + +```java + public int maxProfit(int[] prices) { + if(prices==null||prices.length<=1) {return 0;} + int [][] dp = new int[prices.length][2]; + dp[0][0] = 0; + dp[0][1] = 0 - prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[i][1] = dp[i-1][1] > dp[i-1][0]-prices[i] ? dp[i-1][1] : dp[i-1][0]-prices[i]; + dp[i][0] = dp[i-1][0] > dp[i-1][1]+prices[i] ? dp[i-1][0] : dp[i-1][1]+prices[i]; + } + return dp[prices.length-1][0]; + } +``` + + + +### 421. 数组中两个数的最大异或值 + +https://leetcode-cn.com/problems/maximum-xor-of-two-numbers-in-an-array/ + +给定一个非空数组,数组中元素为 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231 。 + +找到 ai 和aj 最大的异或 (XOR) 运算结果,其中0 ≤ i, j < n 。 + +你能在O(n)的时间解决这个问题吗? + +示例: + +输入: [3, 10, 5, 25, 2, 8] + +输出: 28 + +解释: 最大的结果是 5 ^ 25 = 28 + + + +```java +public static class TreeNode { + public int val; + public TreeNode left = null; + public TreeNode right = null; + public TreeNode(int val) { + this.val = val; + } + } + +Integer findMaximumXOR(int[] array) { + if (array==null||array.length==0) { + return null; + } + TreeNode root = new TreeNode(-1); + //构建前缀树 + for (int i = 0; i < array.length; i++) { + insert(root,array[i]); + } + int max =0; + + for (int i = 0; i < array.length; i++) { + int result = findMaxForTheValue(root,array[i]); + if (result>max){ + max= result; + } + } + return max; +} + +void insert(TreeNode root, int insertValue) { + //最大值是是2的31次方 + int bitValue = 1<<30; + TreeNode currentNode = root; + while (bitValue!=0) { + int result = insertValue & bitValue; + if (result==0) {//array[i]这一位是0,往左创建节点 + if (currentNode.left==null) { + TreeNode node = new TreeNode(-1); + currentNode.left = node; + } + currentNode = currentNode.left; + } else {//array[i]这一位是1,往右边创建节点 + if (currentNode.right==null) { + TreeNode node = new TreeNode(-1); + currentNode.right = node; + } + currentNode = currentNode.right; + } + bitValue= bitValue>>1; + } + currentNode.val = insertValue; +} + +int findMaxForTheValue(TreeNode root, int value) { + TreeNode currentNode = root; + int bitValue = 1<<30; + while (bitValue!=0) { + int result = value & bitValue; + if (result==0) {//array[i]这一位是0,往右边找节点 + + currentNode = currentNode.right != null ? + currentNode.right : currentNode.left; + + } else {//array[i]这一位是1,往左边找节点 + currentNode = currentNode.left != null ? + currentNode.left : currentNode.right; + } + bitValue= bitValue>>1; + } + int result = value^currentNode.val; + return result; +} +``` \ No newline at end of file diff --git a/docs/bookRecommend.md b/docs/bookRecommend.md new file mode 100644 index 0000000..bd2f65a --- /dev/null +++ b/docs/bookRecommend.md @@ -0,0 +1,96 @@ +(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!) + +技术书籍其实有很多,我自己也买了很多,网上也有很多推荐书单,但是每个的情况都不太一样,当前工作中需要深入了解的技术面也不尽相同。所以我就谈一谈我自己买的一些书,以及看的一些书的感受。我的感受是看完一本技术书籍,只算是完成了20%,看完书写读书笔记,也只能算是完成了40%,看完书然后自己找一些面试题,自己翻书查资料,尝试着用自己的语言来讲解面试题,才算是完成了80%。就像在中学时,如果只是上课听懂,或者上课记很多笔记,但是回家从来不做作业,考试肯定得不了高分。所以也欢迎大家一起来完善这个项目! + +### 《深入理解Java虚拟机 第三版》 + + + + - 推荐指数:五星 + - 阅读进度:20% + - 感受:这本书是在2019年双十二的时候出第三版了,作者更新了一些内容,强烈建议大家都买一本来看看,我自己还没有看完。 + +### 《疯狂Java讲义》 + + + +- 推荐指数:五星 + +- 阅读进度:70% + +- 感受:这本书大概看了一半,还没有完全看完,特点是比较全,讲解得比较通俗易懂,也有很多大学使用这本书当做教材。我个人认为对于普通的开发工程师来说,比看《Java编程思想》 ,《Java核心技术 卷I》 要好一些。 + +### 《Java核心技术 卷I》 + + + +* 推荐指数:三星 + +* 阅读进度:100% + +* 感受:这本书是比较好的书,可能国内的编辑翻译得比较生涩,有些地方没有那么好通俗易懂,而且很多地方也不是很深入。我只看了卷I,我觉得还是看看《疯狂Java讲义》要便于理解一些。 + +### 《Effective Java》 + + + +- 推荐指数:四星 + +- 阅读进度:100% + +- 感受:书是好书,但是编辑翻译得让人捉急,可以先看看《疯狂Java讲义》,之后再来看这本书。 + +### 《Redis设计与实现》 + + + + - 推荐指数:五星 + + - 阅读进度:100% + + - 感受:这本书对于了解的Redis原理还是很好的,我觉得可以看一看(里面的原理都是针对于Redis 2.6,可能有一些实现已经遍历,例如在老版本中,List存储的元素较少时,会使用ziplist作为底层实现,元素较多时,使用linkedList来作为底层实现,而在新版本,引入了一种新的数据结构quickList,来作为底层实现。) + +### 《Redis深度历险:核心原理与应用实践》 + + + +- 推荐指数:五星 + +- 阅读进度:100% + +- 感受:这本书是一个掘金作者写的掘金小册,我感觉作者是阅读了《Redis设计与实现》,并且作者也去看了一下源码和其他博客,然后整理写得一个小册,覆盖了Redis的方方面面,比较全面,但是因为篇幅有限,更像是一个概要,快速了解一些技术点,需要深入了解还是需要看《Redis设计与实现》或者自己去资料。 +- 图书链接:https://juejin.im/book/5afc2e5f6fb9a07a9b362527 + +### 《MySQL必知必会》 + + + +- 推荐指数:三星 + +- 阅读进度:100% + +- 感受:这本书讲得比较全面,主要是讲用法,看起来很轻松,如果想要快速了解MySQL的各种用法,可以买一本来看一看。 + +### 《操作系统导论》 + + + +- 推荐指数:五星 + +- 阅读进度:0% + +- 感受:这本书我是买了,但是还没有看,看评价是国外的一对计算机教授夫妇以通俗易懂地方式来讲解操作系统,也是2019年才出中文版的,之前都是Github上的一些爱好者们在自发得对这本书的英文原版翻译。 + +- 豆瓣链接:https://book.douban.com/subject/33463930 + +### 《大话数据结构》 + + + +- 推荐指数:五星 + +- 阅读进度:30% + +- 感受:感觉讲得还比较通俗,需要复习数据结构的朋友可以看一看。 + +还有一些看过的书,之后再来更新了,还有很多书,买了还没有看,看完了再来更新吧。 \ No newline at end of file diff --git a/docs/idgenerator.md b/docs/idgenerator.md new file mode 100644 index 0000000..1025af4 --- /dev/null +++ b/docs/idgenerator.md @@ -0,0 +1,338 @@ +在日常的业务开发中,通常需要对一些数据做唯一标识,例如为大量抓取的文章入库时分配一个唯一的id,为用户下的订单分配订单号等等。并发量小的时候,通常会使用数据库自增的主键id作为唯一id。并发量大的时候就会考虑使用一些分布式ID的生成方案来生成id。由于一些特殊的业务需求,我们的业务中也使用到了分布式ID的生成,对分布式ID的各种方案进行了调研。也对开源的分布式ID框架进行了一些优化和改造。本文主要分为以下三个部分: + +**一.常见的分布式ID生成方案的简单介绍。** + +**二.对当前开源的分布式ID框架实现原理进行分析,例如美团的Leaf和百度的uid-generator。** + +**三.Leaf和uid-generator存在的一些问题及如何进行优化改进。** + +# 一、分布式ID生成方案简介 + +目前常用的分布式ID生成方案主要由以下几种: + +1.使用UUID算法生成唯一id。 + +2.利用单机数据库主键自增来生成唯一id。 + +3.多数据库主键自增生成唯一id。(设置步长区分不同数据库) + +4.数据库分段发号生成唯一id。(例如美团的Leaf框架中的segement模式) + +5.基于snowflake算法生成唯一id(例如美团的Leaf框架中的snowflake模式,百度的uid-generator) + +### 1.UUID + +简单的来说,UUID是服务器在不需要任何外界依赖(像类Snowflake算法的方案都需要注册中心)的情况下,基于当前时间、计数器(counter)和硬件标识等等信息生成的唯一ID。 + +#### 优点 + +##### 无任何依赖 + +其他的技术方案都是有依赖的,比如**单机数据库主键自增生成ID**强依赖数据库,**类Snowflake算法的方案**至少启动时都需要注册中心,Leaf框架Snowflake模式需要定时上传时间戳到注册中心,的UUID生成不需要任何外界依赖, + +#### 缺点 +#### ID太长,且不是数字类型 +当然为了唯一性,带来的牺牲就是生成的结果一般是32位的字符串。由于字符串太长,并且不是数字类型,所以不适合作为数据库的主键。 + +(字符串作为主键id,插入数据时会是在聚集索引中是随机插入,容易造成页分离。而且字符串的比较比数字类型的开销更大,字符串作为主键id查询效率会低于数字类型的主键。) + +#### 适用场景 + +通常可以作为一些临时性唯一标识,例如用户登陆后,生成一个UUID作为登录的会话ID,作为key存储在Redis中,Value是用户相关的信息。 + +### 2.单机数据库主键自增 + +业务量不大时普遍采用这种方案来生成id。 + +#### 优点 +#### 方便接入 +因为一般的项目不一定会用到Zookeeper等这些组件,但是基本都会用到数据库,所以项目接入会比较简单,也没有增加额外的维护成本。 +#### 单调递增 +是绝对的单调递增的,就是从时间线上看,后面生成的id肯定比前面生成的id要大。 +#### 缺点 +#### 强依赖于数据库 +一旦单机数据库发生宕机,就没法生成id,导致整个系统不可用。如果数据库是主从架构的,主库发生故障,切换成从库,如果从库还没来得及收到主库最新的插入id的更新,就有可能导致从库当前的自增id不是最新的,从而生成出重复的id。 + +#### id是连续的 +id是连续的有可能会成为缺点,竞争对手在当天12点下一个订单,然后在第二天12点下一个订单,可能根据订单id的差就可以推测出每天的订单量。像猫眼电影就使用了这种方法来生成电影的id。一般我们日常使用时,其实为了让我们生成id的方式更难被竞争对手猜测出,一般是不会从1开始的,但是猫眼电影这里是从1开始的,而且是连续的,所以我们使用二分法很快就确定了8875是最大的值,也就是总共有8875部电影,而且由于是连续的,爬取也会比较方便。 +猫眼电影的id——也是使用单机数据库生成的,连续自增的 +```HTML +https://maoyan.com/films/1 +https://maoyan.com/films/2 +https://maoyan.com/films/8874 +https://maoyan.com/films/8875 +https://maoyan.com/films/8876 +(1到8875可以请求到数据,8876及后面的id +请求不到数据,没有这些id对应的电影,说明总共有8875部电影) +``` +除非去做额外的处理(例如定时去获取当前的自增起始值a,然后生成一个随机数b,使用`alter table users AUTO_INCREMENT=a+b;`命令对自增起始值修改,也就是跳过一些id。) + +#### 适用场景 +适用于并发量不高的业务。 + +## 3.多数据库主键自增生成唯一id。(设置步长区分不同数据库) +可以使用以下命令设置MySQL中表每次自增时的步长,通过将不同数据库的步长设置为一样,可以让不同数据库生成的id进行区分。 +```sql +CREATE TABLE table (...) AUTO_INCREMENT = n; +alter table auto_increment=2; +``` +![](https://user-gold-cdn.xitu.io/2020/6/21/172d6b9bdc74c246?w=843&h=403&f=png&s=32469) +例如,步长是等于数据库的数量,例如有N台数据库, +第一台数据库的起始值是0,那么生成的id就是0,N,2N,3N等等。 + +第一台数据库的起始值是1,那么生成的id就是1,N+1,2N+1,3N+1等等。 +这样各个数据库生成的id就不会冲突,并且每个数据库可以单独生成id。 + +#### 优点 +生成id的效率比单台数据库要高,因为可以多台数据库同时发号。 + +#### 缺点 +是解决了每次自增是1的问题,缺点是一旦设置了步长,就不方便扩容了,因为分库分表的表的数量已经定下来了。 + +#### 使用场景 +在没有分库分表的框架以前,那些分库分表就是使用这种方案来实现的。 + +## 4.数据库分段发号生成唯一id。(例如美团的Leaf框架中的segement模式) +简单来说,就是想下图一样,用一个数据库表来充当发号器, +表中字段介绍如下: +``` +biz_tag字段用于区分每种id应用的业务, +max_id字段记录了当前已生成的最大的id, +step字段代表每次可以获取id的数量 +``` +![](https://user-gold-cdn.xitu.io/2020/6/21/172d6c5a7ba8b5ef?w=1124&h=180&f=png&s=18129) +id生成项目每次使用下面这条语句从数据库获取step数量的id,并且更新max_id的值,将step数量的id存储在内存中,供业务方通过HTTP,RPC,Client等方式来获取。 +``` +UPDATE leaf_alloc SET max_id = max_id + step WHERE biz_tag = #{tag} +``` +#### 优点 +#### 效率高 +生成id的效率取决于step的大小,不会像主键自增生成id那样再受限于数据库的数量。 + +#### 缺点 +#### 强依赖于数据库 +还是强依赖于数据库,数据库宕机后,虽然id生成系统靠内存中还未使用完id,可以维持系统正常运行一段时间,但是数据库不可用还是会导致整个系统不可用。 + +## 5.基于snowflake算法生成唯一id + +snowflake是推特开源分布式ID生成算法,一共有64位, + +第一位是0,标志位 + +接下来41位是13位的毫秒时间戳,最大可以到2039年9月 + +接下来10个二进制位是服务器的id + +后面12位是业务序列号 + +![](https://user-gold-cdn.xitu.io/2020/6/21/172d63241852055d?w=731&h=181&f=png&s=24076) +意味着每毫秒最大可以生成2的12次方个id,4096个,支持每个机器每毫秒生成4096个id,每秒可以生成400多万的id + +#### 优点 +#### 效率高 +生成id是比较快, +#### 不依赖其他组件 +生成id的过程中,可以做到不额外依赖其他组件。 + + + + + + + + + + + + + + +# 二、开源的分布式ID框架实现原理分析 + +## Leaf +Leaf主要有Leaf-Segment和Leaf-Snowflake两种模式 +## Leaf-Segment +Leaf-Segment主要采用上面的`第四种.数据库分段发号生成唯一id`的技术方案实现的。 + +这是我画的一个执行流程图,左边部分是业务系统获取id的流程: + +1.业务系统通过HTTP,RPC,Client等方式向ID生成系统获取ID。 + +2.判断ID生成系统内存中当前SegmentBuffer使用率是否达到了10%,是的话触发另一个SegmentBuffer的异步更新。 (SegmentBuffer可以认为是一个容器,用于存放每次从数据库取出的step数量的id,然后供业务系统消耗。) + +3.判断当前SegmentBuffer的id是否使用完了,如果没有使用完,就直接从这个SegmentBuffer取一个id返回。否则就将当前SegmentBuffer与备用SegmentBuffer进行交换,然后从交换后的SegmentBuffer取一个id返回给业务系统。 + +右边是备用SegementBuffer的更新流程: + +ID生成系统会判断内存中当前Segement使用率是否达到了10% + + +![](https://user-gold-cdn.xitu.io/2020/6/21/172d7279bf90017b?w=789&h=928&f=png&s=150021) + +**Leaf框架的优化点之一**就是使用了双SegmentBuffer进行优化,如果单个SegmentBuffer,在id消耗完之后,ID生成系统需要从数据库获取新的号段,填充到SegmentBuffer,然后供业务系统消费,此时业务系统发来的请求需要进行等待。如果是双SegmentBuffer,可以在一个SegmentBuffer的id使用率达到阀值(默认是10%),就触发另一个SegmentBuffer的异步更新,提取把号段获取到,存储到内存中。 +## 百度的uid-generator + +##### 默认模式 + +每次启动时向数据库插入一条数据,这行数据的主键是自增的,主键id就是workId, + +因为默认是snowflake算法是1标志位+41位时间戳+10位机器号+12位序列号, + +因为百度的是每次启动都获取新的机器号,所以它修改了这些位数配比,是 + +1标志位+28位的时间差+22位的机器号+13位的序列号,所以总共支出2的22次方次启动,也就是400万次启动。 + +解决时间回拨问题: + +* 启动时时间回拨 + +因为是每次都用新的机器号,所以当前机器号都是之前没有的,所以即便时间戳回拨也不影响。 + +* 运行时时间回拨 + +会使用lastSecond来记录上次生成id的时间戳,如果当前时间戳比lastSecond还小,就抛出异常。 + +##### 缓存模式 + +主要继承自默认模式,只是用一个环形数组来存储生成好的id,每次去环形数组中去,默认大小是2的13次方,8192。这种模式使用的时间取得不是实时的系统时间,而且使用启动时的时间,每次生成一组id时,对之前保存的时间+1。 + +阀值检测,然后填充 + +取id时,可用id数小于阀值50%时,去填充 + +定期填充 + +会去检查环形数组中id使用情况,然后生成一组最大序列号个数的id(默认是8192个),然后进行填充,多的直接丢弃掉, + +### 美团的leaf + +snowflake模式 + +1位的符号位+41位的时间戳+10位的workID+10位的序列号 + + + + + + +#### 1 时钟回拨问题 + +若机器出现时钟回拨,会产生重复id + +##### 1)启动时回拨 + +启动时获取其他机器的时间,计算出平均时间后,将本机时间与平均时间比较,若超过阈值则启动失败。 + +##### 2)运行时回拨 + +1. 直接拒绝,抛异常 + +2. 若回拨时间小于阈值,则睡眠 + +3. 若回拨时间大于阈值,直接拒绝服务并报错,或者更换机器号,或者利用拓展位 + + + +##### 时间类型 + +- 时间戳:自1970.01.01起(今日头条) +- 时间差:自选起始时间(百度、美团) + +##### 2)时间精度 + +- 秒级:支持峰值更高(今日头条、百度) +- 毫秒级:记录时间粒度更细(美团) + +汽车之家是自增的id,只是作品类型不同,文章,视频等 + +头条,百度,美团也是使用了snowFlake只是改变了位数占比。 + +比如头条是去掉了符号标示位,缩短了时间戳,前31位是秒级时间戳+自定义的序列(可能是机器id+递增的数),记录的时间粒度会粗一些,每一秒生成的id会少一些,但是支持的峰值会高一些,时间戳也可以不从1970年开始选,也可以从自定义的时间开始选,这样支持的年长会多一些。 + +#### 3 机器号 + +纯机器号,或,机房号+机器号 + +##### 1)物理机 + +- 手动配置 +- zookeeper(美团) + 在zookeeper如未注册则创建持久顺序节点,顺序号当机器号,并本地缓存 + +##### 2)虚拟机 + +- redis + 利用redis原子计数器,虚拟机启动后,请求计数器,按机器号位数取余 +- zookeeper + 从0到机器号上限,在zookeeper尝试创建临时节点,成功则为当前机器号 +- 数据库(百度) + 利用系统变量的host和port为唯一索引,在数据库中存取,主键作为机器id + + + + + +参考链接: + +https://tech.meituan.com/2017/04/21/mt-leaf.html + + + +缺点是 + +1.趋势递增,容易被猜到订单量,第一条下单多少,第二条下单多少,可以自己加随机数。 + +2.依赖机器时间,如果机器时间出现回拨,变成以前的时间,可能会导致id重复。 + +解决回拨的问题 + +就是机器需要同步时间,不一致会对时间回拨,可能会导致id重复,像百度的snowflake是几台特定的服务器,每次的机器id也是从数据库里面生成的,所以不需要做时间同步。 + + + +#### 3 类snowflake算法 + +采用snowflake算法思想,类snowflake算法根据各自业务需要,做出了不同改动。比如改变了位数占比,添加了业务线等其他信息 + +| 厂商 | 长度 | 组合 | 说明 | +| :----------------------------------------------------------- | :--- | :--------- | :----------------------------------------------------------- | +| [美团Leaf](https://tech.meituan.com/2017/04/21/mt-leaf.html) | 64位 | 1+41+10+12 | 沿用snowflake方案的bit位设计,1符号位+41位毫秒时间戳+10位机器位+12位序列号,只不过41位毫秒时间戳存的是某个指定时间点后的时间差 | +| [百度UidGenerator](https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md) | 64位 | 1+28+22+13 | 1符号位+28秒级时间差(最多可支持约8.7年)+22位机器号(最多可支持约420w次机器启动)+13位序列号 | +| 今日头条-文章 | 64位 | 1+31+32 | 1符号位+31秒级时间戳+32位其他 | +| 今日头条-微头条 | 52位 | 1+31+20 | 1符号位+31秒级时间戳+20位其他 | +| 我们自己的方案 | 54位 | 1+30+10+13 | 其实跟百度的方案类似,只是调小了机器位分配,增大了时间位,因为百度值支持8.7年,我们的可以支持34年生成出来的结果,转换10进制是最小14位,目前最大id是15位(因为目前时间距离自定义的时间点比较近) | + + + +###### 附:位数对应 + +| **二进制** | 45-47位 | 48-50位 | 51-54位 | 55-57位 | 58-60位 | 61-64位 | +| ---------- | ------- | ------- | ------- | ------- | ------- | ------- | +| **十进制** | 14位 | 15位 | 16位 | 17位 | 18位 | 19位 | + + + +调研了Leaf和uid-generator,由于Leaf有额外的zookeeper依赖, + +所以选用了uid-generator,做的改进如下: + +1.原本是一旦时钟回拨就抛出异常,修改为时钟回拨小于1s时,就不抛出抛出异常,进行等待1s。 + +2.加了序列号抛弃策略。按照原有序列号位数分配,是13位,就是每秒可以生成的id数是8192个id,如果并发量比较小,由于每秒获取的id的序列号部分都是从0开始的,或导致后缀0的数据会比较多,容易造成数据倾斜的问题,而且也容易泄露数据信息。可以增加抛弃策略,就是取每一秒的id时,计算一个最大值为序列号的10%随机数,从这个随机数开始取。 + +3.修改了位数分配,原有的时间位是28位的秒级时间差,最长服务年限只支持8.7年,我们把时间差位数分配了30位,最长可以支持34年。原本机器位是22位,支持启动400万次,我们其实不需要那么多次启动,调整成20位,支持100万次启动。1+30+20+13 + +4.增加机器位用完时的取余操作,便于复用。 + + + +leaf的缺点: + +1.号段模式的信息安全性问题,不考虑机器线下和重启丢掉的这些id,id是完全连续的,容易被竞争对手猜到信息安全性。 + +2.小问题修复,就是Leaf为了减少对Zookeeper的依赖,在本地也存了一个上次使用的workid的缓存文件,保证在Zookeeper挂掉的情况下,id生成服务能正常启动,读取本地缓存的workid,然后启动。但是本地缓存文件里面只存储了workid,没有存储上次生成的id的时间戳,假如启动前服务器的时间被修改了,那么启动时就没法对时间进行校验,就会导致生成的id重复。 + +3.缺乏对最大支持年限的检查,时间戳部分溢出会影响符号位,导致生成的id是负数。 + +4.注册中心只支持Zookeeper,issue里面很多人提出,对于他们的项目来说,由于需要引入Zookeeper依赖,增加部署和维护Zookeeper成本。所以我fork了这个项目,增加了使用MySQL作为注册中心,以及本地配置作为注册中心的模块。 + diff --git a/docs/linux.md b/docs/linux.md new file mode 100644 index 0000000..c56e09c --- /dev/null +++ b/docs/linux.md @@ -0,0 +1,40 @@ +### kill pid和kill -9 pid的区别是什么? + +kill pid + +代表通知应用程序自行关闭。系统会发生一个SIGTERM命令给进程对应的应用程序,让应用程序释放自己的资源后,自行关闭。大部分应用程序可能接受后会释放资源,自行停止,也有一些程序不会理会。 + +kill -9 pid + +代表强制关闭进程。系统会发送一个SIGKILL命令给进程。 + +一般建议先执行kill pid,然后过一两秒后等程序做一些释放资源的操作,然后再使用kill -9命令强制删除。 + +https://www.cnblogs.com/aspirant/p/11543456.html + +### 僵尸进程和孤儿进程是什么? +僵尸进程就是子进程调用exit退出或者是运行时发生致命错误,结束运行时,一般会把进程的退出状态通知给操作系统,操作系统发送SIGCHLD信号告诉父进程“子进程退出了”,父进程一般会使用wait系统调用以获得子进程的退出状态,这样内核就可以在内存中释放子进程了,但是如果父进程没有进行wait系统调用,子进程就会驻留在内存,成为僵尸进程。 + +孤儿进程就是父进程退出,但是它的子进程还在进行,这些子进程就会变成孤儿进程,被init进程(进程号为1)所收养,由它来管理和收集子进程的状态。由于孤儿进程有init进程循环的wait()调用回收资源,所以不会产生什么危害。 + +##### Linux指令使用 + +统计access.log中ip访问次数前十的 + +``` +cat access.log | awk '{ print $1}' | sort -n | uniq -c | sort - r |head 10 +``` + +统计当前目录下(包含子目录) java 的文件的代码总行数。 + +``` +wc -l `find . -name "*.java"` | awk '{ sum=sum+$1 } END { print sum }' +``` + +### Linux进程间通信的方式? + +《Linux 的进程间通信》 https://zhuanlan.zhihu.com/p/58489873 + +浅析进程间通信的几种方式(含实例源码) https://zhuanlan.zhihu.com/p/94856678 + +https://mp.weixin.qq.com/s/WgZaS5w5IXa3IBGRsPKtbQ \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..a1aa2b7 --- /dev/null +++ b/index.html @@ -0,0 +1,54 @@ + + + + + 《大厂面试指北》 + + + + + + + +
+ + + + + + + + + \ No newline at end of file diff --git a/introductionimages/1234.gif b/introductionimages/1234.gif deleted file mode 100644 index 17c508b..0000000 Binary files a/introductionimages/1234.gif and /dev/null differ diff --git a/introductionimages/IMG_0345.PNG b/introductionimages/IMG_0345.PNG deleted file mode 100644 index 09a1e65..0000000 Binary files a/introductionimages/IMG_0345.PNG and /dev/null differ diff --git a/introductionimages/IMG_0346.PNG b/introductionimages/IMG_0346.PNG deleted file mode 100644 index ea48114..0000000 Binary files a/introductionimages/IMG_0346.PNG and /dev/null differ diff --git a/introductionimages/IMG_0347.PNG b/introductionimages/IMG_0347.PNG deleted file mode 100644 index 9a8c6e4..0000000 Binary files a/introductionimages/IMG_0347.PNG and /dev/null differ diff --git a/introductionimages/IMG_0349.PNG b/introductionimages/IMG_0349.PNG deleted file mode 100644 index ff00fcc..0000000 Binary files a/introductionimages/IMG_0349.PNG and /dev/null differ diff --git a/readme2.txt b/readme2.txt deleted file mode 100644 index 6528d67..0000000 --- a/readme2.txt +++ /dev/null @@ -1,121 +0,0 @@ - - -# TTNews - -如果下载太慢了,可以来这个网站上下载http://www.codedata.cn/cdetail/Objective-C/Demo/1471438806256270 - - -![image](https://github.com/577528249/TTNews/blob/master/introductionimages/1234.gif) - - -作为一个集新闻与娱乐于一体的新闻客户端, - - -她具有阅读新闻,观赏搞笑图片,观赏搞笑视频等功能, - - -她采用MVC架构,拥有新闻,图片,视频,我四个模块, - - -她所涉及的技术有网络请求,视频播放,数据持久化,陀螺仪监测等, - - -如果您认为她对您有所帮助,希望您能为她点一下star,感谢您的访问! - - -PS:她的新闻数据源来自于百度API Store里的免费新闻api, - -轮播图数据接口http://apistore.baidu.com/apiworks/servicedetail/1570.html - - -新闻接口http://apistore.baidu.com/apiworks/servicedetail/688.html - - - -她的搞笑图片,搞笑视频数据源来自于百思不得姐 -(api由抓包分析网络请求所得,版权归百思不得姐所有), - - -新手项目,多多包涵,谢谢!有相关问题可以在线留言或发邮件至577528249@qq.com,谢谢! - - -#使用方法 - - -点击右上角Download Zip按钮,将项目压缩包下载至本地,解压后点击文件夹中TTNews.xcworkspace即可运行。 - - - - - -# 新闻首页 - - -![image](https://github.com/577528249/TTNews/blob/master/introductionimages/IMG_0345.PNG) - - - - - - - - - - - - - - - - -# 图片界面 - - -![image](https://github.com/577528249/TTNews/blob/master/introductionimages/IMG_0346.PNG) - - - - - - - - - - - - - - - -# 视频界面 - - -![image](https://github.com/577528249/TTNews/blob/master/introductionimages/IMG_0347.PNG) - - - - - - - - - - - -# 我的界面 - -![image](https://github.com/577528249/TTNews/blob/master/introductionimages/IMG_0349.PNG) - - - - -#License -The MIT License (MIT) - -Copyright (c) 2016 YangJunhui - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/static/%E7%B3%BB%E7%BB%9F%E8%BF%9B%E7%A8%8B%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%9B%BE-20200517151907367.png b/static/%E7%B3%BB%E7%BB%9F%E8%BF%9B%E7%A8%8B%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%9B%BE-20200517151907367.png new file mode 100644 index 0000000..02ab4d6 Binary files /dev/null and b/static/%E7%B3%BB%E7%BB%9F%E8%BF%9B%E7%A8%8B%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%9B%BE-20200517151907367.png differ diff --git a/static/%E7%B3%BB%E7%BB%9F%E8%BF%9B%E7%A8%8B%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%9B%BE.png b/static/%E7%B3%BB%E7%BB%9F%E8%BF%9B%E7%A8%8B%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%9B%BE.png new file mode 100644 index 0000000..02ab4d6 Binary files /dev/null and b/static/%E7%B3%BB%E7%BB%9F%E8%BF%9B%E7%A8%8B%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%9B%BE.png differ diff --git a/static/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%B8%BB%E8%A6%81%E7%9A%84%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B-20200726212505246.png b/static/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%B8%BB%E8%A6%81%E7%9A%84%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B-20200726212505246.png new file mode 100644 index 0000000..04fbc2c Binary files /dev/null and b/static/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%B8%BB%E8%A6%81%E7%9A%84%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B-20200726212505246.png differ diff --git a/static/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%B8%BB%E8%A6%81%E7%9A%84%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B.png b/static/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%B8%BB%E8%A6%81%E7%9A%84%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B.png new file mode 100644 index 0000000..04fbc2c Binary files /dev/null and b/static/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%B8%BB%E8%A6%81%E7%9A%84%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B.png differ diff --git a/static/01b10b5a725f31ceaac7247bb9dd0306.png b/static/01b10b5a725f31ceaac7247bb9dd0306.png new file mode 100644 index 0000000..6f696ba Binary files /dev/null and b/static/01b10b5a725f31ceaac7247bb9dd0306.png differ diff --git a/static/0894a336d0cfbf05c51cd8b41d1a8090.png b/static/0894a336d0cfbf05c51cd8b41d1a8090.png new file mode 100644 index 0000000..a57c906 Binary files /dev/null and b/static/0894a336d0cfbf05c51cd8b41d1a8090.png differ diff --git a/static/1.png b/static/1.png new file mode 100644 index 0000000..93c3006 Binary files /dev/null and b/static/1.png differ diff --git a/static/1024555-20161217182750011-675658660.png b/static/1024555-20161217182750011-675658660.png new file mode 100644 index 0000000..29dfa57 Binary files /dev/null and b/static/1024555-20161217182750011-675658660.png differ diff --git a/static/1024555-20161217182857323-2092264199.png b/static/1024555-20161217182857323-2092264199.png new file mode 100644 index 0000000..69924e6 Binary files /dev/null and b/static/1024555-20161217182857323-2092264199.png differ diff --git a/static/1075473-20180726181756270-1907770368.png b/static/1075473-20180726181756270-1907770368.png new file mode 100644 index 0000000..47265f7 Binary files /dev/null and b/static/1075473-20180726181756270-1907770368.png differ diff --git a/static/12609483-008d6304d53ba2a2.png b/static/12609483-008d6304d53ba2a2.png new file mode 100644 index 0000000..9060310 Binary files /dev/null and b/static/12609483-008d6304d53ba2a2.png differ diff --git a/static/12609483-00ab7a17aa0f4d61.png b/static/12609483-00ab7a17aa0f4d61.png new file mode 100644 index 0000000..f2f66e4 Binary files /dev/null and b/static/12609483-00ab7a17aa0f4d61.png differ diff --git a/static/12609483-025e4bf14ce8dc40.png b/static/12609483-025e4bf14ce8dc40.png new file mode 100644 index 0000000..1ca1bc3 Binary files /dev/null and b/static/12609483-025e4bf14ce8dc40.png differ diff --git a/static/12609483-0569068844757572.png b/static/12609483-0569068844757572.png new file mode 100755 index 0000000..634518f Binary files /dev/null and b/static/12609483-0569068844757572.png differ diff --git a/static/12609483-05767921ed3dad2a.png b/static/12609483-05767921ed3dad2a.png new file mode 100644 index 0000000..12b9c0a Binary files /dev/null and b/static/12609483-05767921ed3dad2a.png differ diff --git a/static/12609483-05e1a6bce1c29051.png b/static/12609483-05e1a6bce1c29051.png new file mode 100644 index 0000000..58e9a50 Binary files /dev/null and b/static/12609483-05e1a6bce1c29051.png differ diff --git a/static/12609483-05ffe2378a7a63eb.png b/static/12609483-05ffe2378a7a63eb.png new file mode 100644 index 0000000..96d9074 Binary files /dev/null and b/static/12609483-05ffe2378a7a63eb.png differ diff --git a/static/12609483-072e5f8dfe85b67a.png b/static/12609483-072e5f8dfe85b67a.png new file mode 100644 index 0000000..7920288 Binary files /dev/null and b/static/12609483-072e5f8dfe85b67a.png differ diff --git a/static/12609483-0734e513fe56a194.png b/static/12609483-0734e513fe56a194.png new file mode 100644 index 0000000..a02d76c Binary files /dev/null and b/static/12609483-0734e513fe56a194.png differ diff --git a/static/12609483-08f52cca19a78a04.png b/static/12609483-08f52cca19a78a04.png new file mode 100644 index 0000000..b055bb1 Binary files /dev/null and b/static/12609483-08f52cca19a78a04.png differ diff --git a/static/12609483-0b76181613ab9b88.png b/static/12609483-0b76181613ab9b88.png new file mode 100644 index 0000000..5c3dba6 Binary files /dev/null and b/static/12609483-0b76181613ab9b88.png differ diff --git a/static/12609483-1048ece433706d22.png b/static/12609483-1048ece433706d22.png new file mode 100644 index 0000000..a0e47a7 Binary files /dev/null and b/static/12609483-1048ece433706d22.png differ diff --git a/static/12609483-1134d72ac589b510.png b/static/12609483-1134d72ac589b510.png new file mode 100644 index 0000000..4d88539 Binary files /dev/null and b/static/12609483-1134d72ac589b510.png differ diff --git a/static/12609483-1200a07007edb639.png b/static/12609483-1200a07007edb639.png new file mode 100755 index 0000000..e112f42 Binary files /dev/null and b/static/12609483-1200a07007edb639.png differ diff --git a/static/12609483-13b966cad4481566.png b/static/12609483-13b966cad4481566.png new file mode 100644 index 0000000..8346185 Binary files /dev/null and b/static/12609483-13b966cad4481566.png differ diff --git a/static/12609483-15f831546f662ac9.png b/static/12609483-15f831546f662ac9.png new file mode 100644 index 0000000..5672d4c Binary files /dev/null and b/static/12609483-15f831546f662ac9.png differ diff --git a/static/12609483-16cd9cf2320a5c0b.png b/static/12609483-16cd9cf2320a5c0b.png new file mode 100755 index 0000000..80b4b14 Binary files /dev/null and b/static/12609483-16cd9cf2320a5c0b.png differ diff --git a/static/12609483-1707e382f5058cf9.png b/static/12609483-1707e382f5058cf9.png new file mode 100644 index 0000000..712073e Binary files /dev/null and b/static/12609483-1707e382f5058cf9.png differ diff --git a/static/12609483-178537093cafab1a.png b/static/12609483-178537093cafab1a.png new file mode 100755 index 0000000..853ca5a Binary files /dev/null and b/static/12609483-178537093cafab1a.png differ diff --git a/static/12609483-17b68c0d6e4a8c69.png b/static/12609483-17b68c0d6e4a8c69.png new file mode 100644 index 0000000..eb3383a Binary files /dev/null and b/static/12609483-17b68c0d6e4a8c69.png differ diff --git a/static/12609483-1829498245a5787b.png b/static/12609483-1829498245a5787b.png new file mode 100755 index 0000000..3ccd70f Binary files /dev/null and b/static/12609483-1829498245a5787b.png differ diff --git a/static/12609483-1867c2cea2e07c0f.png b/static/12609483-1867c2cea2e07c0f.png new file mode 100644 index 0000000..1df0145 Binary files /dev/null and b/static/12609483-1867c2cea2e07c0f.png differ diff --git a/static/12609483-1942478235258bac.png b/static/12609483-1942478235258bac.png new file mode 100644 index 0000000..46b066c Binary files /dev/null and b/static/12609483-1942478235258bac.png differ diff --git a/static/12609483-199bd1a12bee5cc3.png b/static/12609483-199bd1a12bee5cc3.png new file mode 100755 index 0000000..546147b Binary files /dev/null and b/static/12609483-199bd1a12bee5cc3.png differ diff --git a/static/12609483-19da8a08046df094.png b/static/12609483-19da8a08046df094.png new file mode 100644 index 0000000..07cb95b Binary files /dev/null and b/static/12609483-19da8a08046df094.png differ diff --git a/static/12609483-1dbb238efbfcfdfc.png b/static/12609483-1dbb238efbfcfdfc.png new file mode 100644 index 0000000..f6f9e06 Binary files /dev/null and b/static/12609483-1dbb238efbfcfdfc.png differ diff --git a/static/12609483-1ef1d215da9c4aa4.png b/static/12609483-1ef1d215da9c4aa4.png new file mode 100644 index 0000000..c6a71f5 Binary files /dev/null and b/static/12609483-1ef1d215da9c4aa4.png differ diff --git a/static/12609483-21d0ef9d804fa018.png b/static/12609483-21d0ef9d804fa018.png new file mode 100644 index 0000000..e867948 Binary files /dev/null and b/static/12609483-21d0ef9d804fa018.png differ diff --git a/static/12609483-231d3ec0b1d24103.png b/static/12609483-231d3ec0b1d24103.png new file mode 100644 index 0000000..ef05b58 Binary files /dev/null and b/static/12609483-231d3ec0b1d24103.png differ diff --git a/static/12609483-232a5ad60979603b.png b/static/12609483-232a5ad60979603b.png new file mode 100644 index 0000000..1b2c004 Binary files /dev/null and b/static/12609483-232a5ad60979603b.png differ diff --git a/static/12609483-2353b802cd0c8d1a.png b/static/12609483-2353b802cd0c8d1a.png new file mode 100644 index 0000000..8b200c6 Binary files /dev/null and b/static/12609483-2353b802cd0c8d1a.png differ diff --git a/static/12609483-247cddd987b8a7dc.png b/static/12609483-247cddd987b8a7dc.png new file mode 100644 index 0000000..c887cd6 Binary files /dev/null and b/static/12609483-247cddd987b8a7dc.png differ diff --git a/static/12609483-25a4fa2be26ec351.png b/static/12609483-25a4fa2be26ec351.png new file mode 100644 index 0000000..c8d3033 Binary files /dev/null and b/static/12609483-25a4fa2be26ec351.png differ diff --git a/static/12609483-2650e43d71ae45e3.png b/static/12609483-2650e43d71ae45e3.png new file mode 100644 index 0000000..d7a3d65 Binary files /dev/null and b/static/12609483-2650e43d71ae45e3.png differ diff --git a/static/12609483-27d2db1766354b36.png b/static/12609483-27d2db1766354b36.png new file mode 100644 index 0000000..5662ced Binary files /dev/null and b/static/12609483-27d2db1766354b36.png differ diff --git a/static/12609483-293bd833c87dda4c.png b/static/12609483-293bd833c87dda4c.png new file mode 100644 index 0000000..da999b8 Binary files /dev/null and b/static/12609483-293bd833c87dda4c.png differ diff --git a/static/12609483-2b5a351a9cea481f.png b/static/12609483-2b5a351a9cea481f.png new file mode 100644 index 0000000..6c14d62 Binary files /dev/null and b/static/12609483-2b5a351a9cea481f.png differ diff --git a/static/12609483-2bbc4ac66f3fc019.png b/static/12609483-2bbc4ac66f3fc019.png new file mode 100755 index 0000000..9b20950 Binary files /dev/null and b/static/12609483-2bbc4ac66f3fc019.png differ diff --git a/static/12609483-2c54436d6108d7f1.png b/static/12609483-2c54436d6108d7f1.png new file mode 100644 index 0000000..346911c Binary files /dev/null and b/static/12609483-2c54436d6108d7f1.png differ diff --git a/static/12609483-2cc427780dabbd24.png b/static/12609483-2cc427780dabbd24.png new file mode 100644 index 0000000..28680bb Binary files /dev/null and b/static/12609483-2cc427780dabbd24.png differ diff --git a/static/12609483-2e06d5c0cc55c67b.png b/static/12609483-2e06d5c0cc55c67b.png new file mode 100644 index 0000000..5ae95e4 Binary files /dev/null and b/static/12609483-2e06d5c0cc55c67b.png differ diff --git a/static/12609483-31c0f328654affff.png b/static/12609483-31c0f328654affff.png new file mode 100644 index 0000000..ec75db5 Binary files /dev/null and b/static/12609483-31c0f328654affff.png differ diff --git a/static/12609483-31cdd1cc821d9c8d.png b/static/12609483-31cdd1cc821d9c8d.png new file mode 100755 index 0000000..f9f1985 Binary files /dev/null and b/static/12609483-31cdd1cc821d9c8d.png differ diff --git a/static/12609483-3230f1f23b429264.png b/static/12609483-3230f1f23b429264.png new file mode 100644 index 0000000..a1eec37 Binary files /dev/null and b/static/12609483-3230f1f23b429264.png differ diff --git a/static/12609483-3232802e8c4f14a5-9624639.png b/static/12609483-3232802e8c4f14a5-9624639.png new file mode 100644 index 0000000..2e8b65b Binary files /dev/null and b/static/12609483-3232802e8c4f14a5-9624639.png differ diff --git a/static/12609483-3232802e8c4f14a5.png b/static/12609483-3232802e8c4f14a5.png new file mode 100644 index 0000000..2e8b65b Binary files /dev/null and b/static/12609483-3232802e8c4f14a5.png differ diff --git a/static/12609483-3403c7d1aac4d4d1.png b/static/12609483-3403c7d1aac4d4d1.png new file mode 100644 index 0000000..4d1e5a4 Binary files /dev/null and b/static/12609483-3403c7d1aac4d4d1.png differ diff --git a/static/12609483-354d0c595c824428.png b/static/12609483-354d0c595c824428.png new file mode 100644 index 0000000..a158c22 Binary files /dev/null and b/static/12609483-354d0c595c824428.png differ diff --git a/static/12609483-36a4169adca877d7.png b/static/12609483-36a4169adca877d7.png new file mode 100644 index 0000000..1a63930 Binary files /dev/null and b/static/12609483-36a4169adca877d7.png differ diff --git a/static/12609483-36e827ab2773002a.png b/static/12609483-36e827ab2773002a.png new file mode 100644 index 0000000..16116ab Binary files /dev/null and b/static/12609483-36e827ab2773002a.png differ diff --git a/static/12609483-376f1967e3784961.png b/static/12609483-376f1967e3784961.png new file mode 100644 index 0000000..47c7366 Binary files /dev/null and b/static/12609483-376f1967e3784961.png differ diff --git a/static/12609483-37c40b2102434890.png b/static/12609483-37c40b2102434890.png new file mode 100644 index 0000000..c778372 Binary files /dev/null and b/static/12609483-37c40b2102434890.png differ diff --git a/static/12609483-3850a513cef3ae99.png b/static/12609483-3850a513cef3ae99.png new file mode 100644 index 0000000..18274fc Binary files /dev/null and b/static/12609483-3850a513cef3ae99.png differ diff --git a/static/12609483-3b8557ed89a19b8a.png b/static/12609483-3b8557ed89a19b8a.png new file mode 100755 index 0000000..196cc23 Binary files /dev/null and b/static/12609483-3b8557ed89a19b8a.png differ diff --git a/static/12609483-3b85e8021cd8e8d0.png b/static/12609483-3b85e8021cd8e8d0.png new file mode 100644 index 0000000..e4440b7 Binary files /dev/null and b/static/12609483-3b85e8021cd8e8d0.png differ diff --git a/static/12609483-3d649e6e34f13ce9.png b/static/12609483-3d649e6e34f13ce9.png new file mode 100644 index 0000000..940850b Binary files /dev/null and b/static/12609483-3d649e6e34f13ce9.png differ diff --git a/static/12609483-3e2f092a320b1904.png b/static/12609483-3e2f092a320b1904.png new file mode 100644 index 0000000..4132213 Binary files /dev/null and b/static/12609483-3e2f092a320b1904.png differ diff --git a/static/12609483-43f76e0c7cc3f4d5.png b/static/12609483-43f76e0c7cc3f4d5.png new file mode 100644 index 0000000..378b521 Binary files /dev/null and b/static/12609483-43f76e0c7cc3f4d5.png differ diff --git a/static/12609483-45e8e5dfa59aa8d3.png b/static/12609483-45e8e5dfa59aa8d3.png new file mode 100755 index 0000000..b344ef2 Binary files /dev/null and b/static/12609483-45e8e5dfa59aa8d3.png differ diff --git a/static/12609483-47571d940363c993.png b/static/12609483-47571d940363c993.png new file mode 100644 index 0000000..bb21c4c Binary files /dev/null and b/static/12609483-47571d940363c993.png differ diff --git a/static/12609483-49b9a8d5e04cce51.png b/static/12609483-49b9a8d5e04cce51.png new file mode 100755 index 0000000..b79ad4e Binary files /dev/null and b/static/12609483-49b9a8d5e04cce51.png differ diff --git a/static/12609483-4af826237ddda349.png b/static/12609483-4af826237ddda349.png new file mode 100644 index 0000000..e2ea32d Binary files /dev/null and b/static/12609483-4af826237ddda349.png differ diff --git a/static/12609483-4c56c2be3b977b77.png b/static/12609483-4c56c2be3b977b77.png new file mode 100644 index 0000000..0ee3176 Binary files /dev/null and b/static/12609483-4c56c2be3b977b77.png differ diff --git a/static/12609483-4e2f87d8d19f6349.png b/static/12609483-4e2f87d8d19f6349.png new file mode 100644 index 0000000..8eadb0e Binary files /dev/null and b/static/12609483-4e2f87d8d19f6349.png differ diff --git a/static/12609483-4e35333651c29262.png b/static/12609483-4e35333651c29262.png new file mode 100644 index 0000000..45a9c7f Binary files /dev/null and b/static/12609483-4e35333651c29262.png differ diff --git a/static/12609483-4ecd4a7222b8642c.png b/static/12609483-4ecd4a7222b8642c.png new file mode 100644 index 0000000..6a86a7f Binary files /dev/null and b/static/12609483-4ecd4a7222b8642c.png differ diff --git a/static/12609483-4f9566693b78aa1c.png b/static/12609483-4f9566693b78aa1c.png new file mode 100755 index 0000000..86f843c Binary files /dev/null and b/static/12609483-4f9566693b78aa1c.png differ diff --git a/static/12609483-51f11addf81dd7bf.png b/static/12609483-51f11addf81dd7bf.png new file mode 100644 index 0000000..d266ceb Binary files /dev/null and b/static/12609483-51f11addf81dd7bf.png differ diff --git a/static/12609483-525097b9e86c4bcb.png b/static/12609483-525097b9e86c4bcb.png new file mode 100644 index 0000000..4a7c0c3 Binary files /dev/null and b/static/12609483-525097b9e86c4bcb.png differ diff --git a/static/12609483-529e2cf20c540145.png b/static/12609483-529e2cf20c540145.png new file mode 100644 index 0000000..5a49422 Binary files /dev/null and b/static/12609483-529e2cf20c540145.png differ diff --git a/static/12609483-5593c6a8abdc9c5d.png b/static/12609483-5593c6a8abdc9c5d.png new file mode 100644 index 0000000..6f22790 Binary files /dev/null and b/static/12609483-5593c6a8abdc9c5d.png differ diff --git a/static/12609483-55942da80ed331dc.png b/static/12609483-55942da80ed331dc.png new file mode 100755 index 0000000..c396b84 Binary files /dev/null and b/static/12609483-55942da80ed331dc.png differ diff --git a/static/12609483-55a1e1bbfe1627f6.png b/static/12609483-55a1e1bbfe1627f6.png new file mode 100644 index 0000000..eea4d12 Binary files /dev/null and b/static/12609483-55a1e1bbfe1627f6.png differ diff --git a/static/12609483-5758b86fedd81704.png b/static/12609483-5758b86fedd81704.png new file mode 100644 index 0000000..edccf18 Binary files /dev/null and b/static/12609483-5758b86fedd81704.png differ diff --git a/static/12609483-5a15791898c0a7cf.png b/static/12609483-5a15791898c0a7cf.png new file mode 100644 index 0000000..e5c4b72 Binary files /dev/null and b/static/12609483-5a15791898c0a7cf.png differ diff --git a/static/12609483-5d5aecc951557f56.png b/static/12609483-5d5aecc951557f56.png new file mode 100644 index 0000000..e1de1bd Binary files /dev/null and b/static/12609483-5d5aecc951557f56.png differ diff --git a/static/12609483-5fdce25bd4b132d4.png b/static/12609483-5fdce25bd4b132d4.png new file mode 100644 index 0000000..97a35f8 Binary files /dev/null and b/static/12609483-5fdce25bd4b132d4.png differ diff --git a/static/12609483-6264bb0a50df8696.png b/static/12609483-6264bb0a50df8696.png new file mode 100644 index 0000000..4e08b60 Binary files /dev/null and b/static/12609483-6264bb0a50df8696.png differ diff --git a/static/12609483-62986b21673088de.png b/static/12609483-62986b21673088de.png new file mode 100644 index 0000000..0fd31b2 Binary files /dev/null and b/static/12609483-62986b21673088de.png differ diff --git a/static/12609483-6305ecdf2f8995f9.png b/static/12609483-6305ecdf2f8995f9.png new file mode 100644 index 0000000..fde3be6 Binary files /dev/null and b/static/12609483-6305ecdf2f8995f9.png differ diff --git a/static/12609483-6453143eae73ed6f.png b/static/12609483-6453143eae73ed6f.png new file mode 100644 index 0000000..5c38750 Binary files /dev/null and b/static/12609483-6453143eae73ed6f.png differ diff --git a/static/12609483-6576136fd07e6686.png b/static/12609483-6576136fd07e6686.png new file mode 100644 index 0000000..8fc168d Binary files /dev/null and b/static/12609483-6576136fd07e6686.png differ diff --git a/static/12609483-659c79a0d288942a.png b/static/12609483-659c79a0d288942a.png new file mode 100644 index 0000000..f6a3250 Binary files /dev/null and b/static/12609483-659c79a0d288942a.png differ diff --git a/static/12609483-665f81152013e93c.png b/static/12609483-665f81152013e93c.png new file mode 100644 index 0000000..300f488 Binary files /dev/null and b/static/12609483-665f81152013e93c.png differ diff --git a/static/12609483-66ad206714156a51.png b/static/12609483-66ad206714156a51.png new file mode 100644 index 0000000..e4d7f7e Binary files /dev/null and b/static/12609483-66ad206714156a51.png differ diff --git a/static/12609483-66b5c2d55ba4d32a.png b/static/12609483-66b5c2d55ba4d32a.png new file mode 100644 index 0000000..58138ea Binary files /dev/null and b/static/12609483-66b5c2d55ba4d32a.png differ diff --git a/static/12609483-67fdc1962265c48c.png b/static/12609483-67fdc1962265c48c.png new file mode 100644 index 0000000..feb20e2 Binary files /dev/null and b/static/12609483-67fdc1962265c48c.png differ diff --git a/static/12609483-684e68614fb23645-9624353-9624370-9624377-9624398-9624404.png b/static/12609483-684e68614fb23645-9624353-9624370-9624377-9624398-9624404.png new file mode 100644 index 0000000..370d82a Binary files /dev/null and b/static/12609483-684e68614fb23645-9624353-9624370-9624377-9624398-9624404.png differ diff --git a/static/12609483-684e68614fb23645-9624353-9624370-9624377-9624398.png b/static/12609483-684e68614fb23645-9624353-9624370-9624377-9624398.png new file mode 100644 index 0000000..370d82a Binary files /dev/null and b/static/12609483-684e68614fb23645-9624353-9624370-9624377-9624398.png differ diff --git a/static/12609483-684e68614fb23645-9624353-9624370-9624377.png b/static/12609483-684e68614fb23645-9624353-9624370-9624377.png new file mode 100644 index 0000000..370d82a Binary files /dev/null and b/static/12609483-684e68614fb23645-9624353-9624370-9624377.png differ diff --git a/static/12609483-684e68614fb23645-9624353-9624370.png b/static/12609483-684e68614fb23645-9624353-9624370.png new file mode 100644 index 0000000..370d82a Binary files /dev/null and b/static/12609483-684e68614fb23645-9624353-9624370.png differ diff --git a/static/12609483-684e68614fb23645-9624353.png b/static/12609483-684e68614fb23645-9624353.png new file mode 100644 index 0000000..370d82a Binary files /dev/null and b/static/12609483-684e68614fb23645-9624353.png differ diff --git a/static/12609483-684e68614fb23645.png b/static/12609483-684e68614fb23645.png new file mode 100644 index 0000000..370d82a Binary files /dev/null and b/static/12609483-684e68614fb23645.png differ diff --git a/static/12609483-69338fc49a4509a6.png b/static/12609483-69338fc49a4509a6.png new file mode 100644 index 0000000..364806f Binary files /dev/null and b/static/12609483-69338fc49a4509a6.png differ diff --git a/static/12609483-69f72d04b83e18d4.png b/static/12609483-69f72d04b83e18d4.png new file mode 100644 index 0000000..5afb5db Binary files /dev/null and b/static/12609483-69f72d04b83e18d4.png differ diff --git a/static/12609483-6d1abd9d8ecbe769.png b/static/12609483-6d1abd9d8ecbe769.png new file mode 100755 index 0000000..8757c4c Binary files /dev/null and b/static/12609483-6d1abd9d8ecbe769.png differ diff --git a/static/12609483-6d3ccb46dfb9e99b.png b/static/12609483-6d3ccb46dfb9e99b.png new file mode 100755 index 0000000..e2b50e8 Binary files /dev/null and b/static/12609483-6d3ccb46dfb9e99b.png differ diff --git a/static/12609483-6de69717b1e77bc6.png b/static/12609483-6de69717b1e77bc6.png new file mode 100644 index 0000000..1281670 Binary files /dev/null and b/static/12609483-6de69717b1e77bc6.png differ diff --git a/static/12609483-6f4a20afd0628edf.png b/static/12609483-6f4a20afd0628edf.png new file mode 100644 index 0000000..626f341 Binary files /dev/null and b/static/12609483-6f4a20afd0628edf.png differ diff --git a/static/12609483-6f7331032865b57b.png b/static/12609483-6f7331032865b57b.png new file mode 100644 index 0000000..c3dc34d Binary files /dev/null and b/static/12609483-6f7331032865b57b.png differ diff --git a/static/12609483-71bd98f13bbe8fa9.png b/static/12609483-71bd98f13bbe8fa9.png new file mode 100644 index 0000000..bb5b46a Binary files /dev/null and b/static/12609483-71bd98f13bbe8fa9.png differ diff --git a/static/12609483-71e11f3b05fdd87b.png b/static/12609483-71e11f3b05fdd87b.png new file mode 100644 index 0000000..6b816ba Binary files /dev/null and b/static/12609483-71e11f3b05fdd87b.png differ diff --git a/static/12609483-73f1c52b4c03b738.png b/static/12609483-73f1c52b4c03b738.png new file mode 100644 index 0000000..5b8bbff Binary files /dev/null and b/static/12609483-73f1c52b4c03b738.png differ diff --git a/static/12609483-78e1877dec1728ca.png b/static/12609483-78e1877dec1728ca.png new file mode 100755 index 0000000..f56319e Binary files /dev/null and b/static/12609483-78e1877dec1728ca.png differ diff --git a/static/12609483-79f894260bd7a035-9624539.png b/static/12609483-79f894260bd7a035-9624539.png new file mode 100644 index 0000000..4ff1772 Binary files /dev/null and b/static/12609483-79f894260bd7a035-9624539.png differ diff --git a/static/12609483-79f894260bd7a035.png b/static/12609483-79f894260bd7a035.png new file mode 100644 index 0000000..4ff1772 Binary files /dev/null and b/static/12609483-79f894260bd7a035.png differ diff --git a/static/12609483-7c3063235346d100.png b/static/12609483-7c3063235346d100.png new file mode 100644 index 0000000..96fa8d5 Binary files /dev/null and b/static/12609483-7c3063235346d100.png differ diff --git a/static/12609483-810b67dd5e9b46e9.png b/static/12609483-810b67dd5e9b46e9.png new file mode 100644 index 0000000..997c362 Binary files /dev/null and b/static/12609483-810b67dd5e9b46e9.png differ diff --git a/static/12609483-847d713416f5fdff.png b/static/12609483-847d713416f5fdff.png new file mode 100644 index 0000000..044595d Binary files /dev/null and b/static/12609483-847d713416f5fdff.png differ diff --git a/static/12609483-84857b07e81cc097.png b/static/12609483-84857b07e81cc097.png new file mode 100644 index 0000000..c662411 Binary files /dev/null and b/static/12609483-84857b07e81cc097.png differ diff --git a/static/12609483-883506f456096074.png b/static/12609483-883506f456096074.png new file mode 100755 index 0000000..9d4edaf Binary files /dev/null and b/static/12609483-883506f456096074.png differ diff --git a/static/12609483-892ba4177926f667.png b/static/12609483-892ba4177926f667.png new file mode 100644 index 0000000..88edd79 Binary files /dev/null and b/static/12609483-892ba4177926f667.png differ diff --git a/static/12609483-8f63c4fe8cf743f5.png b/static/12609483-8f63c4fe8cf743f5.png new file mode 100644 index 0000000..ca18271 Binary files /dev/null and b/static/12609483-8f63c4fe8cf743f5.png differ diff --git a/static/12609483-90c7eac6c6d06828-9624547.png b/static/12609483-90c7eac6c6d06828-9624547.png new file mode 100644 index 0000000..64f288e Binary files /dev/null and b/static/12609483-90c7eac6c6d06828-9624547.png differ diff --git a/static/12609483-90c7eac6c6d06828.png b/static/12609483-90c7eac6c6d06828.png new file mode 100644 index 0000000..64f288e Binary files /dev/null and b/static/12609483-90c7eac6c6d06828.png differ diff --git a/static/12609483-92df97def8212433.png b/static/12609483-92df97def8212433.png new file mode 100644 index 0000000..f51a4f1 Binary files /dev/null and b/static/12609483-92df97def8212433.png differ diff --git a/static/12609483-97f154661ed5ecd0.png b/static/12609483-97f154661ed5ecd0.png new file mode 100644 index 0000000..a7907e2 Binary files /dev/null and b/static/12609483-97f154661ed5ecd0.png differ diff --git a/static/12609483-9cef67ecb52a18dc.png b/static/12609483-9cef67ecb52a18dc.png new file mode 100644 index 0000000..63e8379 Binary files /dev/null and b/static/12609483-9cef67ecb52a18dc.png differ diff --git a/static/12609483-9daf19837b2b6b8d.png b/static/12609483-9daf19837b2b6b8d.png new file mode 100644 index 0000000..bca1dae Binary files /dev/null and b/static/12609483-9daf19837b2b6b8d.png differ diff --git a/static/12609483-9f59a2ed26c36799.png b/static/12609483-9f59a2ed26c36799.png new file mode 100644 index 0000000..5aa2884 Binary files /dev/null and b/static/12609483-9f59a2ed26c36799.png differ diff --git a/static/12609483-a02d8ee4216a48af.png b/static/12609483-a02d8ee4216a48af.png new file mode 100644 index 0000000..2d1e5ca Binary files /dev/null and b/static/12609483-a02d8ee4216a48af.png differ diff --git a/static/12609483-a0cfc3f69174cec6.png b/static/12609483-a0cfc3f69174cec6.png new file mode 100644 index 0000000..17bdeee Binary files /dev/null and b/static/12609483-a0cfc3f69174cec6.png differ diff --git a/static/12609483-a1800dd48244e22d.png b/static/12609483-a1800dd48244e22d.png new file mode 100644 index 0000000..51e821d Binary files /dev/null and b/static/12609483-a1800dd48244e22d.png differ diff --git a/static/12609483-a2cd8a6bb15c770c.png b/static/12609483-a2cd8a6bb15c770c.png new file mode 100644 index 0000000..b15d7ff Binary files /dev/null and b/static/12609483-a2cd8a6bb15c770c.png differ diff --git a/static/12609483-a5913adb192d42b8.png b/static/12609483-a5913adb192d42b8.png new file mode 100644 index 0000000..ba9a4b1 Binary files /dev/null and b/static/12609483-a5913adb192d42b8.png differ diff --git a/static/12609483-a5f0454ae3254fb5.png b/static/12609483-a5f0454ae3254fb5.png new file mode 100644 index 0000000..628b75e Binary files /dev/null and b/static/12609483-a5f0454ae3254fb5.png differ diff --git a/static/12609483-a7bf91e24890bdf5.png b/static/12609483-a7bf91e24890bdf5.png new file mode 100644 index 0000000..c7ff66d Binary files /dev/null and b/static/12609483-a7bf91e24890bdf5.png differ diff --git a/static/12609483-ab790e64ff6b6e17.png b/static/12609483-ab790e64ff6b6e17.png new file mode 100644 index 0000000..9aa255f Binary files /dev/null and b/static/12609483-ab790e64ff6b6e17.png differ diff --git a/static/12609483-ab82c3317f8d0b3c.png b/static/12609483-ab82c3317f8d0b3c.png new file mode 100644 index 0000000..3da2cf1 Binary files /dev/null and b/static/12609483-ab82c3317f8d0b3c.png differ diff --git a/static/12609483-ac3e3e5a7b3b427e.png b/static/12609483-ac3e3e5a7b3b427e.png new file mode 100644 index 0000000..a2ee586 Binary files /dev/null and b/static/12609483-ac3e3e5a7b3b427e.png differ diff --git a/static/12609483-ad60c82070093caf.png b/static/12609483-ad60c82070093caf.png new file mode 100644 index 0000000..ed99283 Binary files /dev/null and b/static/12609483-ad60c82070093caf.png differ diff --git a/static/12609483-af0238d029c8e4e7.png b/static/12609483-af0238d029c8e4e7.png new file mode 100644 index 0000000..ca9743d Binary files /dev/null and b/static/12609483-af0238d029c8e4e7.png differ diff --git a/static/12609483-af8591f99a0ef451.png b/static/12609483-af8591f99a0ef451.png new file mode 100644 index 0000000..8e4f37d Binary files /dev/null and b/static/12609483-af8591f99a0ef451.png differ diff --git a/static/12609483-b05ceaabc0e3b6a6.png b/static/12609483-b05ceaabc0e3b6a6.png new file mode 100755 index 0000000..290fa23 Binary files /dev/null and b/static/12609483-b05ceaabc0e3b6a6.png differ diff --git a/static/12609483-b0ccf7f7ee0ed9cb.png b/static/12609483-b0ccf7f7ee0ed9cb.png new file mode 100755 index 0000000..0ff0e40 Binary files /dev/null and b/static/12609483-b0ccf7f7ee0ed9cb.png differ diff --git a/static/12609483-b142de351caa4bdc.png b/static/12609483-b142de351caa4bdc.png new file mode 100755 index 0000000..ca606ea Binary files /dev/null and b/static/12609483-b142de351caa4bdc.png differ diff --git a/static/12609483-b15a13b54ef52bcd.png b/static/12609483-b15a13b54ef52bcd.png new file mode 100644 index 0000000..bd66c3d Binary files /dev/null and b/static/12609483-b15a13b54ef52bcd.png differ diff --git a/static/12609483-b1fe8217cff50300.png b/static/12609483-b1fe8217cff50300.png new file mode 100644 index 0000000..255df6b Binary files /dev/null and b/static/12609483-b1fe8217cff50300.png differ diff --git a/static/12609483-b201b1635c4b2125.png b/static/12609483-b201b1635c4b2125.png new file mode 100644 index 0000000..95fffbc Binary files /dev/null and b/static/12609483-b201b1635c4b2125.png differ diff --git a/static/12609483-b20cd2594752fad8.png b/static/12609483-b20cd2594752fad8.png new file mode 100644 index 0000000..b186f3a Binary files /dev/null and b/static/12609483-b20cd2594752fad8.png differ diff --git a/static/12609483-b4263d32e9ae0d5f.png b/static/12609483-b4263d32e9ae0d5f.png new file mode 100644 index 0000000..607b376 Binary files /dev/null and b/static/12609483-b4263d32e9ae0d5f.png differ diff --git a/static/12609483-b4d2e3ca90ae600b.png b/static/12609483-b4d2e3ca90ae600b.png new file mode 100755 index 0000000..0ec150a Binary files /dev/null and b/static/12609483-b4d2e3ca90ae600b.png differ diff --git a/static/12609483-b587ac0f04f12a51.png b/static/12609483-b587ac0f04f12a51.png new file mode 100644 index 0000000..2e40dc7 Binary files /dev/null and b/static/12609483-b587ac0f04f12a51.png differ diff --git a/static/12609483-b683204e668de0d9.png b/static/12609483-b683204e668de0d9.png new file mode 100644 index 0000000..b4e223e Binary files /dev/null and b/static/12609483-b683204e668de0d9.png differ diff --git a/static/12609483-bbdf1cc5ac1a4ef0.png b/static/12609483-bbdf1cc5ac1a4ef0.png new file mode 100755 index 0000000..dc10072 Binary files /dev/null and b/static/12609483-bbdf1cc5ac1a4ef0.png differ diff --git a/static/12609483-bd74b09698d89898.png b/static/12609483-bd74b09698d89898.png new file mode 100644 index 0000000..7004932 Binary files /dev/null and b/static/12609483-bd74b09698d89898.png differ diff --git a/static/12609483-bd97bcf1a54970ab.png b/static/12609483-bd97bcf1a54970ab.png new file mode 100644 index 0000000..8280590 Binary files /dev/null and b/static/12609483-bd97bcf1a54970ab.png differ diff --git a/static/12609483-c3480da40113b984.png b/static/12609483-c3480da40113b984.png new file mode 100644 index 0000000..d627057 Binary files /dev/null and b/static/12609483-c3480da40113b984.png differ diff --git a/static/12609483-c41c27cd14c59c4d.png b/static/12609483-c41c27cd14c59c4d.png new file mode 100755 index 0000000..4317e59 Binary files /dev/null and b/static/12609483-c41c27cd14c59c4d.png differ diff --git a/static/12609483-c4adf66192e77225.png b/static/12609483-c4adf66192e77225.png new file mode 100644 index 0000000..135e42d Binary files /dev/null and b/static/12609483-c4adf66192e77225.png differ diff --git a/static/12609483-c50ea2a774c79417.png b/static/12609483-c50ea2a774c79417.png new file mode 100644 index 0000000..cac99a8 Binary files /dev/null and b/static/12609483-c50ea2a774c79417.png differ diff --git a/static/12609483-c73d7b850397558a.png b/static/12609483-c73d7b850397558a.png new file mode 100644 index 0000000..1e17db8 Binary files /dev/null and b/static/12609483-c73d7b850397558a.png differ diff --git a/static/12609483-c914918f6dfecfc5.png b/static/12609483-c914918f6dfecfc5.png new file mode 100644 index 0000000..0ea6196 Binary files /dev/null and b/static/12609483-c914918f6dfecfc5.png differ diff --git a/static/12609483-c93badac7f87621c.png b/static/12609483-c93badac7f87621c.png new file mode 100644 index 0000000..1be64f2 Binary files /dev/null and b/static/12609483-c93badac7f87621c.png differ diff --git a/static/12609483-cbcfb324cea0f1bb.png b/static/12609483-cbcfb324cea0f1bb.png new file mode 100644 index 0000000..eccfbf4 Binary files /dev/null and b/static/12609483-cbcfb324cea0f1bb.png differ diff --git a/static/12609483-cd0ab58840e43364.png b/static/12609483-cd0ab58840e43364.png new file mode 100644 index 0000000..b9324ba Binary files /dev/null and b/static/12609483-cd0ab58840e43364.png differ diff --git a/static/12609483-ce42aae525de4f33.png b/static/12609483-ce42aae525de4f33.png new file mode 100644 index 0000000..0b3f9bf Binary files /dev/null and b/static/12609483-ce42aae525de4f33.png differ diff --git a/static/12609483-d09569b3e780120b.png b/static/12609483-d09569b3e780120b.png new file mode 100644 index 0000000..6a80ddf Binary files /dev/null and b/static/12609483-d09569b3e780120b.png differ diff --git a/static/12609483-d18fbaa9eb21d3cf.png b/static/12609483-d18fbaa9eb21d3cf.png new file mode 100644 index 0000000..5519760 Binary files /dev/null and b/static/12609483-d18fbaa9eb21d3cf.png differ diff --git a/static/12609483-d2dd19e0d29e9b1e-9624693.png b/static/12609483-d2dd19e0d29e9b1e-9624693.png new file mode 100644 index 0000000..7568711 Binary files /dev/null and b/static/12609483-d2dd19e0d29e9b1e-9624693.png differ diff --git a/static/12609483-d2dd19e0d29e9b1e.png b/static/12609483-d2dd19e0d29e9b1e.png new file mode 100644 index 0000000..7568711 Binary files /dev/null and b/static/12609483-d2dd19e0d29e9b1e.png differ diff --git a/static/12609483-d2ec43d839dc3f9c.png b/static/12609483-d2ec43d839dc3f9c.png new file mode 100644 index 0000000..db7c6d7 Binary files /dev/null and b/static/12609483-d2ec43d839dc3f9c.png differ diff --git a/static/12609483-d36a73cde36c31ec.png b/static/12609483-d36a73cde36c31ec.png new file mode 100644 index 0000000..0643087 Binary files /dev/null and b/static/12609483-d36a73cde36c31ec.png differ diff --git a/static/12609483-d3f2cd0e07a28217.png b/static/12609483-d3f2cd0e07a28217.png new file mode 100644 index 0000000..8334aae Binary files /dev/null and b/static/12609483-d3f2cd0e07a28217.png differ diff --git a/static/12609483-d66c9d1ce490a96c.png b/static/12609483-d66c9d1ce490a96c.png new file mode 100644 index 0000000..01e7b90 Binary files /dev/null and b/static/12609483-d66c9d1ce490a96c.png differ diff --git a/static/12609483-d84b28aaf117b063.png b/static/12609483-d84b28aaf117b063.png new file mode 100644 index 0000000..8f282e7 Binary files /dev/null and b/static/12609483-d84b28aaf117b063.png differ diff --git a/static/12609483-daa1017965210d79.png b/static/12609483-daa1017965210d79.png new file mode 100644 index 0000000..bbd61c1 Binary files /dev/null and b/static/12609483-daa1017965210d79.png differ diff --git a/static/12609483-dc91fefa6c8067c2.png b/static/12609483-dc91fefa6c8067c2.png new file mode 100755 index 0000000..3b35f21 Binary files /dev/null and b/static/12609483-dc91fefa6c8067c2.png differ diff --git a/static/12609483-dcb976d2039fbc30.png b/static/12609483-dcb976d2039fbc30.png new file mode 100644 index 0000000..6486380 Binary files /dev/null and b/static/12609483-dcb976d2039fbc30.png differ diff --git a/static/12609483-df3aa06eac8be3cc.png b/static/12609483-df3aa06eac8be3cc.png new file mode 100644 index 0000000..32be0e0 Binary files /dev/null and b/static/12609483-df3aa06eac8be3cc.png differ diff --git a/static/12609483-e174c1c5d4283c0d.png b/static/12609483-e174c1c5d4283c0d.png new file mode 100644 index 0000000..2f510ab Binary files /dev/null and b/static/12609483-e174c1c5d4283c0d.png differ diff --git a/static/12609483-e3090ff08c7c67c5.png b/static/12609483-e3090ff08c7c67c5.png new file mode 100644 index 0000000..c41e594 Binary files /dev/null and b/static/12609483-e3090ff08c7c67c5.png differ diff --git a/static/12609483-e796b57f35c2bacc.png b/static/12609483-e796b57f35c2bacc.png new file mode 100644 index 0000000..c153615 Binary files /dev/null and b/static/12609483-e796b57f35c2bacc.png differ diff --git a/static/12609483-e831050cd046fede.png b/static/12609483-e831050cd046fede.png new file mode 100644 index 0000000..15c85cb Binary files /dev/null and b/static/12609483-e831050cd046fede.png differ diff --git a/static/12609483-e9060f6bdd9b95fa.png b/static/12609483-e9060f6bdd9b95fa.png new file mode 100644 index 0000000..75dda5e Binary files /dev/null and b/static/12609483-e9060f6bdd9b95fa.png differ diff --git a/static/12609483-e996e9bd2572dc30-9624620.png b/static/12609483-e996e9bd2572dc30-9624620.png new file mode 100644 index 0000000..c164484 Binary files /dev/null and b/static/12609483-e996e9bd2572dc30-9624620.png differ diff --git a/static/12609483-e996e9bd2572dc30.png b/static/12609483-e996e9bd2572dc30.png new file mode 100644 index 0000000..c164484 Binary files /dev/null and b/static/12609483-e996e9bd2572dc30.png differ diff --git a/static/12609483-e9a01efc5be2b1b0.png b/static/12609483-e9a01efc5be2b1b0.png new file mode 100644 index 0000000..456b44e Binary files /dev/null and b/static/12609483-e9a01efc5be2b1b0.png differ diff --git a/static/12609483-ee0a70ad12f8437c.png b/static/12609483-ee0a70ad12f8437c.png new file mode 100644 index 0000000..c9d4357 Binary files /dev/null and b/static/12609483-ee0a70ad12f8437c.png differ diff --git a/static/12609483-ef708d9fdeca1857.png b/static/12609483-ef708d9fdeca1857.png new file mode 100644 index 0000000..4dbe10b Binary files /dev/null and b/static/12609483-ef708d9fdeca1857.png differ diff --git a/static/12609483-efff2ee9978ac306.png b/static/12609483-efff2ee9978ac306.png new file mode 100644 index 0000000..8ee34af Binary files /dev/null and b/static/12609483-efff2ee9978ac306.png differ diff --git a/static/12609483-f12252aa0fbd3724.png b/static/12609483-f12252aa0fbd3724.png new file mode 100755 index 0000000..87d3bfd Binary files /dev/null and b/static/12609483-f12252aa0fbd3724.png differ diff --git a/static/12609483-f49199a436960c2b.png b/static/12609483-f49199a436960c2b.png new file mode 100644 index 0000000..160f53b Binary files /dev/null and b/static/12609483-f49199a436960c2b.png differ diff --git a/static/12609483-f4dc3c2e06dc055e.png b/static/12609483-f4dc3c2e06dc055e.png new file mode 100644 index 0000000..2c3613c Binary files /dev/null and b/static/12609483-f4dc3c2e06dc055e.png differ diff --git a/static/12609483-f60146d5eaffeba0.png b/static/12609483-f60146d5eaffeba0.png new file mode 100644 index 0000000..5498d0d Binary files /dev/null and b/static/12609483-f60146d5eaffeba0.png differ diff --git a/static/12609483-f62b69326ef4844d.png b/static/12609483-f62b69326ef4844d.png new file mode 100644 index 0000000..fe2d235 Binary files /dev/null and b/static/12609483-f62b69326ef4844d.png differ diff --git a/static/12609483-f63c9314fc23399e.png b/static/12609483-f63c9314fc23399e.png new file mode 100644 index 0000000..a27fc18 Binary files /dev/null and b/static/12609483-f63c9314fc23399e.png differ diff --git a/static/12609483-f802533e7bf1b7f7.png b/static/12609483-f802533e7bf1b7f7.png new file mode 100644 index 0000000..d7dfd03 Binary files /dev/null and b/static/12609483-f802533e7bf1b7f7.png differ diff --git a/static/12609483-fbee5c2cfb8601da.png b/static/12609483-fbee5c2cfb8601da.png new file mode 100644 index 0000000..d818d97 Binary files /dev/null and b/static/12609483-fbee5c2cfb8601da.png differ diff --git a/static/12609483-fc2ea16adcffe4b5.png b/static/12609483-fc2ea16adcffe4b5.png new file mode 100644 index 0000000..4b3dff1 Binary files /dev/null and b/static/12609483-fc2ea16adcffe4b5.png differ diff --git a/static/12609483-fea86f8efeff63c0.png b/static/12609483-fea86f8efeff63c0.png new file mode 100644 index 0000000..3eb018b Binary files /dev/null and b/static/12609483-fea86f8efeff63c0.png differ diff --git a/static/12609483-fff5e4e51fd4b74f.png b/static/12609483-fff5e4e51fd4b74f.png new file mode 100644 index 0000000..e1c4791 Binary files /dev/null and b/static/12609483-fff5e4e51fd4b74f.png differ diff --git a/static/13002258-3191bc567f09cd99.png b/static/13002258-3191bc567f09cd99.png new file mode 100644 index 0000000..020bfcd Binary files /dev/null and b/static/13002258-3191bc567f09cd99.png differ diff --git a/static/13002258-488a103decf49453.png b/static/13002258-488a103decf49453.png new file mode 100644 index 0000000..20c3f5a Binary files /dev/null and b/static/13002258-488a103decf49453.png differ diff --git a/static/1415794-20190804110605330-45276489.png b/static/1415794-20190804110605330-45276489.png new file mode 100755 index 0000000..b87395d Binary files /dev/null and b/static/1415794-20190804110605330-45276489.png differ diff --git a/static/1460000008942623.jpeg b/static/1460000008942623.jpeg new file mode 100644 index 0000000..4cc1cda Binary files /dev/null and b/static/1460000008942623.jpeg differ diff --git a/static/1581500802565.jpg b/static/1581500802565.jpg new file mode 100644 index 0000000..703ac87 Binary files /dev/null and b/static/1581500802565.jpg differ diff --git a/static/160_statement.png b/static/160_statement.png new file mode 100644 index 0000000..55870e3 Binary files /dev/null and b/static/160_statement.png differ diff --git a/static/16315cb9175365f5.png b/static/16315cb9175365f5.png new file mode 100755 index 0000000..f26a3cf Binary files /dev/null and b/static/16315cb9175365f5.png differ diff --git a/static/16315cb9193719c2.png b/static/16315cb9193719c2.png new file mode 100755 index 0000000..4a407c4 Binary files /dev/null and b/static/16315cb9193719c2.png differ diff --git a/static/16bd188f482703e1.png b/static/16bd188f482703e1.png new file mode 100644 index 0000000..1e17db8 Binary files /dev/null and b/static/16bd188f482703e1.png differ diff --git a/static/16f98e68c8fed611.png b/static/16f98e68c8fed611.png new file mode 100644 index 0000000..c1dd044 Binary files /dev/null and b/static/16f98e68c8fed611.png differ diff --git a/static/171cf4968afd910e.jpeg b/static/171cf4968afd910e.jpeg new file mode 100644 index 0000000..908ae13 Binary files /dev/null and b/static/171cf4968afd910e.jpeg differ diff --git a/static/1b7102166835857ff8e2597db27e970b.png b/static/1b7102166835857ff8e2597db27e970b.png new file mode 100644 index 0000000..2afd8f4 Binary files /dev/null and b/static/1b7102166835857ff8e2597db27e970b.png differ diff --git a/static/2.png b/static/2.png new file mode 100755 index 0000000..2a0f4f1 Binary files /dev/null and b/static/2.png differ diff --git a/static/20160505123459787.jpeg b/static/20160505123459787.jpeg new file mode 100644 index 0000000..4c82793 Binary files /dev/null and b/static/20160505123459787.jpeg differ diff --git a/static/2018051322430635.png b/static/2018051322430635.png new file mode 100644 index 0000000..28c10a1 Binary files /dev/null and b/static/2018051322430635.png differ diff --git a/static/20190822142826661.png b/static/20190822142826661.png new file mode 100644 index 0000000..ff3d060 Binary files /dev/null and b/static/20190822142826661.png differ diff --git a/static/20190828160405711.png b/static/20190828160405711.png new file mode 100644 index 0000000..265ab3e Binary files /dev/null and b/static/20190828160405711.png differ diff --git a/static/20190828160521218.png b/static/20190828160521218.png new file mode 100644 index 0000000..fdbcc5c Binary files /dev/null and b/static/20190828160521218.png differ diff --git a/static/20190930010605363.jpeg b/static/20190930010605363.jpeg new file mode 100644 index 0000000..1ab115c Binary files /dev/null and b/static/20190930010605363.jpeg differ diff --git a/static/249993-20161215143120620-1544337380-20201130113344624.png b/static/249993-20161215143120620-1544337380-20201130113344624.png new file mode 100644 index 0000000..430502b Binary files /dev/null and b/static/249993-20161215143120620-1544337380-20201130113344624.png differ diff --git a/static/249993-20161215143120620-1544337380.png b/static/249993-20161215143120620-1544337380.png new file mode 100644 index 0000000..430502b Binary files /dev/null and b/static/249993-20161215143120620-1544337380.png differ diff --git a/static/2579123-e0b8898d895aee05.png b/static/2579123-e0b8898d895aee05.png new file mode 100644 index 0000000..80cd5f7 Binary files /dev/null and b/static/2579123-e0b8898d895aee05.png differ diff --git a/static/2b995dc63cc6f999a810396859e9f868.png b/static/2b995dc63cc6f999a810396859e9f868.png new file mode 100644 index 0000000..69697ab Binary files /dev/null and b/static/2b995dc63cc6f999a810396859e9f868.png differ diff --git a/static/3.png b/static/3.png new file mode 100755 index 0000000..cfb7052 Binary files /dev/null and b/static/3.png differ diff --git a/static/3190591-11cabdfdce46f976.JPG b/static/3190591-11cabdfdce46f976.JPG new file mode 100644 index 0000000..07468ad Binary files /dev/null and b/static/3190591-11cabdfdce46f976.JPG differ diff --git a/static/31bad766983e212431077ca8da92762050214.png b/static/31bad766983e212431077ca8da92762050214.png new file mode 100644 index 0000000..fbe43d0 Binary files /dev/null and b/static/31bad766983e212431077ca8da92762050214.png differ diff --git a/static/351684196936_pic.jpg b/static/351684196936_pic.jpg new file mode 100644 index 0000000..434bb31 Binary files /dev/null and b/static/351684196936_pic.jpg differ diff --git a/static/4.png b/static/4.png new file mode 100644 index 0000000..e09aff1 Binary files /dev/null and b/static/4.png differ diff --git a/static/4491294-8edc15f60a58bd0b.png b/static/4491294-8edc15f60a58bd0b.png new file mode 100755 index 0000000..d22e437 Binary files /dev/null and b/static/4491294-8edc15f60a58bd0b.png differ diff --git a/static/4621.png b/static/4621.png new file mode 100644 index 0000000..41dfd24 Binary files /dev/null and b/static/4621.png differ diff --git a/static/519126-20180623154635076-953076776.png b/static/519126-20180623154635076-953076776.png new file mode 100755 index 0000000..8f94267 Binary files /dev/null and b/static/519126-20180623154635076-953076776.png differ diff --git a/static/5401975-4c082ac80e1c042c.png b/static/5401975-4c082ac80e1c042c.png new file mode 100644 index 0000000..e8d25b9 Binary files /dev/null and b/static/5401975-4c082ac80e1c042c.png differ diff --git a/static/5bff9535e4b04dd2799a6ae8.png b/static/5bff9535e4b04dd2799a6ae8.png new file mode 100644 index 0000000..14c9057 Binary files /dev/null and b/static/5bff9535e4b04dd2799a6ae8.png differ diff --git a/static/6111.jpeg b/static/6111.jpeg new file mode 100644 index 0000000..368dee2 Binary files /dev/null and b/static/6111.jpeg differ diff --git a/static/640 b/static/640 new file mode 100644 index 0000000..ad20ba4 Binary files /dev/null and b/static/640 differ diff --git a/static/640-1445199.jpeg b/static/640-1445199.jpeg new file mode 100644 index 0000000..368f2ee Binary files /dev/null and b/static/640-1445199.jpeg differ diff --git a/static/640-1487645. b/static/640-1487645. new file mode 100644 index 0000000..bc450b0 Binary files /dev/null and b/static/640-1487645. differ diff --git a/static/640-1751368.png b/static/640-1751368.png new file mode 100644 index 0000000..eb28c20 Binary files /dev/null and b/static/640-1751368.png differ diff --git a/static/640-20200510171526372.jpeg b/static/640-20200510171526372.jpeg new file mode 100644 index 0000000..f8600b1 Binary files /dev/null and b/static/640-20200510171526372.jpeg differ diff --git a/static/640-20200606144302960.png b/static/640-20200606144302960.png new file mode 100644 index 0000000..30afe93 Binary files /dev/null and b/static/640-20200606144302960.png differ diff --git a/static/640-20200711182519652.jpeg b/static/640-20200711182519652.jpeg new file mode 100644 index 0000000..f23dc3f Binary files /dev/null and b/static/640-20200711182519652.jpeg differ diff --git a/static/640-20200712194015324.png b/static/640-20200712194015324.png new file mode 100644 index 0000000..f1ec5fe Binary files /dev/null and b/static/640-20200712194015324.png differ diff --git a/static/640-20200726210041919.jpeg b/static/640-20200726210041919.jpeg new file mode 100644 index 0000000..037a935 Binary files /dev/null and b/static/640-20200726210041919.jpeg differ diff --git a/static/640-20200726210058837.jpeg b/static/640-20200726210058837.jpeg new file mode 100644 index 0000000..9a64783 Binary files /dev/null and b/static/640-20200726210058837.jpeg differ diff --git a/static/640-20200728210136673.jpeg b/static/640-20200728210136673.jpeg new file mode 100644 index 0000000..c7d8c49 Binary files /dev/null and b/static/640-20200728210136673.jpeg differ diff --git a/static/640-20201128120828308 b/static/640-20201128120828308 new file mode 100644 index 0000000..92c186d Binary files /dev/null and b/static/640-20201128120828308 differ diff --git a/static/640-20201128120828359 b/static/640-20201128120828359 new file mode 100644 index 0000000..78b37d8 Binary files /dev/null and b/static/640-20201128120828359 differ diff --git a/static/640-20201128120828561.png b/static/640-20201128120828561.png new file mode 100644 index 0000000..ad7faaa Binary files /dev/null and b/static/640-20201128120828561.png differ diff --git a/static/640-20210124192752928 b/static/640-20210124192752928 new file mode 100644 index 0000000..8906ec1 Binary files /dev/null and b/static/640-20210124192752928 differ diff --git a/static/640-20210124203435521 b/static/640-20210124203435521 new file mode 100644 index 0000000..51a3329 Binary files /dev/null and b/static/640-20210124203435521 differ diff --git a/static/640-20210221154207974.png b/static/640-20210221154207974.png new file mode 100644 index 0000000..e1c83ee Binary files /dev/null and b/static/640-20210221154207974.png differ diff --git a/static/640-20210326173442637 b/static/640-20210326173442637 new file mode 100644 index 0000000..d7af96e Binary files /dev/null and b/static/640-20210326173442637 differ diff --git a/static/640-20210326191349615 b/static/640-20210326191349615 new file mode 100644 index 0000000..3bfce30 Binary files /dev/null and b/static/640-20210326191349615 differ diff --git a/static/640-2913012.png b/static/640-2913012.png new file mode 100644 index 0000000..10edaad Binary files /dev/null and b/static/640-2913012.png differ diff --git a/static/640-2921069.jpeg b/static/640-2921069.jpeg new file mode 100644 index 0000000..8fd4fd2 Binary files /dev/null and b/static/640-2921069.jpeg differ diff --git a/static/640-3893313.png b/static/640-3893313.png new file mode 100644 index 0000000..5055f5c Binary files /dev/null and b/static/640-3893313.png differ diff --git a/static/640-4460300.jpeg b/static/640-4460300.jpeg new file mode 100644 index 0000000..e6fe874 Binary files /dev/null and b/static/640-4460300.jpeg differ diff --git a/static/640-4553045.png b/static/640-4553045.png new file mode 100644 index 0000000..843b1ab Binary files /dev/null and b/static/640-4553045.png differ diff --git a/static/640-4554190.jpeg b/static/640-4554190.jpeg new file mode 100644 index 0000000..add810b Binary files /dev/null and b/static/640-4554190.jpeg differ diff --git a/static/640-5050715.png b/static/640-5050715.png new file mode 100644 index 0000000..6198d73 Binary files /dev/null and b/static/640-5050715.png differ diff --git a/static/640-5667220.jpeg b/static/640-5667220.jpeg new file mode 100644 index 0000000..6d16994 Binary files /dev/null and b/static/640-5667220.jpeg differ diff --git a/static/640-6358760.png b/static/640-6358760.png new file mode 100644 index 0000000..066a459 Binary files /dev/null and b/static/640-6358760.png differ diff --git a/static/640-6536508.png b/static/640-6536508.png new file mode 100644 index 0000000..8801d69 Binary files /dev/null and b/static/640-6536508.png differ diff --git a/static/640-8127488. b/static/640-8127488. new file mode 100644 index 0000000..3b54fcc Binary files /dev/null and b/static/640-8127488. differ diff --git a/static/640-8127813.png b/static/640-8127813.png new file mode 100644 index 0000000..93498ba Binary files /dev/null and b/static/640-8127813.png differ diff --git a/static/640.jpeg b/static/640.jpeg new file mode 100644 index 0000000..339bdcc Binary files /dev/null and b/static/640.jpeg differ diff --git a/static/640.png b/static/640.png new file mode 100644 index 0000000..d966b22 Binary files /dev/null and b/static/640.png differ diff --git "a/static/640\347\232\204\345\211\257\346\234\254" "b/static/640\347\232\204\345\211\257\346\234\254" new file mode 100644 index 0000000..d50172b Binary files /dev/null and "b/static/640\347\232\204\345\211\257\346\234\254" differ diff --git a/static/66b6ae1dec5b96084c3a6d29174a20e3.png b/static/66b6ae1dec5b96084c3a6d29174a20e3.png new file mode 100644 index 0000000..c1a33ae Binary files /dev/null and b/static/66b6ae1dec5b96084c3a6d29174a20e3.png differ diff --git a/static/6734122-55c8aed7986edbcb.png b/static/6734122-55c8aed7986edbcb.png new file mode 100755 index 0000000..f461cfd Binary files /dev/null and b/static/6734122-55c8aed7986edbcb.png differ diff --git a/static/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f31312f32382f313637353964643162306239363236383f773d37323026683d32353026663d6a70656726733d3337323831.jpeg b/static/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f31312f32382f313637353964643162306239363236383f773d37323026683d32353026663d6a70656726733d3337323831.jpeg new file mode 100644 index 0000000..f673ac6 Binary files /dev/null and b/static/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f31312f32382f313637353964643162306239363236383f773d37323026683d32353026663d6a70656726733d3337323831.jpeg differ diff --git a/static/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f31312f32382f313637353964643162323461633733643f773d38363926683d33353126663d706e6726733d3331313531.png b/static/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f31312f32382f313637353964643162323461633733643f773d38363926683d33353126663d706e6726733d3331313531.png new file mode 100644 index 0000000..69fae87 Binary files /dev/null and b/static/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f31312f32382f313637353964643162323461633733643f773d38363926683d33353126663d706e6726733d3331313531.png differ diff --git a/static/70.png b/static/70.png new file mode 100644 index 0000000..2aa4959 Binary files /dev/null and b/static/70.png differ diff --git a/static/70b12d74e6433967e7d8d115425ae028.png b/static/70b12d74e6433967e7d8d115425ae028.png new file mode 100755 index 0000000..d93d768 Binary files /dev/null and b/static/70b12d74e6433967e7d8d115425ae028.png differ diff --git a/static/721ceeff.png b/static/721ceeff.png new file mode 100644 index 0000000..d964cfb Binary files /dev/null and b/static/721ceeff.png differ diff --git a/static/7779607-7a5ce353116237e2.png b/static/7779607-7a5ce353116237e2.png new file mode 100644 index 0000000..6fd2018 Binary files /dev/null and b/static/7779607-7a5ce353116237e2.png differ diff --git a/static/7779607-dab8f35ecb417433.png b/static/7779607-dab8f35ecb417433.png new file mode 100644 index 0000000..6daf5d3 Binary files /dev/null and b/static/7779607-dab8f35ecb417433.png differ diff --git a/static/8ef32f580f57ceb214c22f7e8c18e80e.png b/static/8ef32f580f57ceb214c22f7e8c18e80e.png new file mode 100644 index 0000000..5cdcd87 Binary files /dev/null and b/static/8ef32f580f57ceb214c22f7e8c18e80e.png differ diff --git a/static/9379fe1666818237f842138812bf63bd85645.png b/static/9379fe1666818237f842138812bf63bd85645.png new file mode 100644 index 0000000..beeec85 Binary files /dev/null and b/static/9379fe1666818237f842138812bf63bd85645.png differ diff --git a/static/ABUIABAEGAAg0MKrwAUosJqimgYwgAU4xQI.png b/static/ABUIABAEGAAg0MKrwAUosJqimgYwgAU4xQI.png new file mode 100644 index 0000000..d870984 Binary files /dev/null and b/static/ABUIABAEGAAg0MKrwAUosJqimgYwgAU4xQI.png differ diff --git a/static/ABUIABAEGAAg7MKrwAUogfiO-QUwgAU48gI.png b/static/ABUIABAEGAAg7MKrwAUogfiO-QUwgAU48gI.png new file mode 100644 index 0000000..170eb3d Binary files /dev/null and b/static/ABUIABAEGAAg7MKrwAUogfiO-QUwgAU48gI.png differ diff --git a/static/ABUIABAEGAAg_sKrwAUojMPt1gIwgAU4mgM.png b/static/ABUIABAEGAAg_sKrwAUojMPt1gIwgAU4mgM.png new file mode 100644 index 0000000..f148588 Binary files /dev/null and b/static/ABUIABAEGAAg_sKrwAUojMPt1gIwgAU4mgM.png differ diff --git a/static/ABUIABAEGAAglcOrwAUoqPiNiQYwgAU4lgM.png b/static/ABUIABAEGAAglcOrwAUoqPiNiQYwgAU4lgM.png new file mode 100644 index 0000000..fb74595 Binary files /dev/null and b/static/ABUIABAEGAAglcOrwAUoqPiNiQYwgAU4lgM.png differ diff --git a/static/ABUIABAEGAAgvMOrwAUoypzSBzCABTjtAg.png b/static/ABUIABAEGAAgvMOrwAUoypzSBzCABTjtAg.png new file mode 100644 index 0000000..be7215e Binary files /dev/null and b/static/ABUIABAEGAAgvMOrwAUoypzSBzCABTjtAg.png differ diff --git a/static/SouthEast.png b/static/SouthEast.png new file mode 100644 index 0000000..c0b111b Binary files /dev/null and b/static/SouthEast.png differ diff --git a/static/WechatIMG0328.jpeg b/static/WechatIMG0328.jpeg new file mode 100755 index 0000000..e100b6c Binary files /dev/null and b/static/WechatIMG0328.jpeg differ diff --git a/static/a8773912b31bb05136157756082ed7b24aede072.jpeg b/static/a8773912b31bb05136157756082ed7b24aede072.jpeg new file mode 100644 index 0000000..5446960 Binary files /dev/null and b/static/a8773912b31bb05136157756082ed7b24aede072.jpeg differ diff --git a/static/articleRead.png b/static/articleRead.png new file mode 100644 index 0000000..89fc4c8 Binary files /dev/null and b/static/articleRead.png differ diff --git a/static/asd21.png b/static/asd21.png new file mode 100644 index 0000000..04fbc2c Binary files /dev/null and b/static/asd21.png differ diff --git a/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F368dee24bab62b1c27fd4a8387cea9619605195f.jpg b/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F368dee24bab62b1c27fd4a8387cea9619605195f.jpg new file mode 100644 index 0000000..368dee2 Binary files /dev/null and b/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F368dee24bab62b1c27fd4a8387cea9619605195f.jpg differ diff --git a/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F5381a5e30482682c1c6f111e882991113b8661f7-20201216185759959.png b/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F5381a5e30482682c1c6f111e882991113b8661f7-20201216185759959.png new file mode 100644 index 0000000..5381a5e Binary files /dev/null and b/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F5381a5e30482682c1c6f111e882991113b8661f7-20201216185759959.png differ diff --git a/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F5381a5e30482682c1c6f111e882991113b8661f7-20201216185819903.png b/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F5381a5e30482682c1c6f111e882991113b8661f7-20201216185819903.png new file mode 100644 index 0000000..5381a5e Binary files /dev/null and b/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F5381a5e30482682c1c6f111e882991113b8661f7-20201216185819903.png differ diff --git a/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F5381a5e30482682c1c6f111e882991113b8661f7.png b/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F5381a5e30482682c1c6f111e882991113b8661f7.png new file mode 100644 index 0000000..5381a5e Binary files /dev/null and b/static/assets%2F-MNvWgO3xPDngyns_iic%2Fsync%2F5381a5e30482682c1c6f111e882991113b8661f7.png differ diff --git a/static/bd319063b4d107251f125efe1cf25d9b.png b/static/bd319063b4d107251f125efe1cf25d9b.png new file mode 100644 index 0000000..9fb368c Binary files /dev/null and b/static/bd319063b4d107251f125efe1cf25d9b.png differ diff --git a/static/circularlinkedlist-9062165.png b/static/circularlinkedlist-9062165.png new file mode 100644 index 0000000..b0111ae Binary files /dev/null and b/static/circularlinkedlist-9062165.png differ diff --git a/static/circularlinkedlist.png b/static/circularlinkedlist.png new file mode 100644 index 0000000..b0111ae Binary files /dev/null and b/static/circularlinkedlist.png differ diff --git a/static/coin.png b/static/coin.png new file mode 100644 index 0000000..5381a5e Binary files /dev/null and b/static/coin.png differ diff --git a/static/collect.png b/static/collect.png new file mode 100644 index 0000000..717f31b Binary files /dev/null and b/static/collect.png differ diff --git a/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark-20210316161015142.image b/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark-20210316161015142.image new file mode 100644 index 0000000..817d252 Binary files /dev/null and b/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark-20210316161015142.image differ diff --git a/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark.image b/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark.image new file mode 100644 index 0000000..817d252 Binary files /dev/null and b/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark.image differ diff --git a/static/dfd3e5cfe90dd475e1ffe7336e7af330-1.png b/static/dfd3e5cfe90dd475e1ffe7336e7af330-1.png new file mode 100755 index 0000000..7925aca Binary files /dev/null and b/static/dfd3e5cfe90dd475e1ffe7336e7af330-1.png differ diff --git a/static/docsify-copy-code.min.js b/static/docsify-copy-code.min.js new file mode 100644 index 0000000..dee84c7 --- /dev/null +++ b/static/docsify-copy-code.min.js @@ -0,0 +1,9 @@ +/*! + * docsify-copy-code + * v2.1.0 + * https://github.com/jperasmus/docsify-copy-code + * (c) 2017-2019 JP Erasmus + * MIT license + */ +!function(){"use strict";function r(o){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(o){return typeof o}:function(o){return o&&"function"==typeof Symbol&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o})(o)}!function(o,e){void 0===e&&(e={});var t=e.insertAt;if(o&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],c=document.createElement("style");c.type="text/css","top"===t&&n.firstChild?n.insertBefore(c,n.firstChild):n.appendChild(c),c.styleSheet?c.styleSheet.cssText=o:c.appendChild(document.createTextNode(o))}}(".docsify-copy-code-button,.docsify-copy-code-button span{cursor:pointer;transition:all .25s ease}.docsify-copy-code-button{position:absolute;z-index:1;top:0;right:0;overflow:visible;padding:.65em .8em;border:0;border-radius:0;outline:0;font-size:1em;background:grey;background:var(--theme-color,grey);color:#fff;opacity:0}.docsify-copy-code-button span{border-radius:3px;background:inherit;pointer-events:none}.docsify-copy-code-button .error,.docsify-copy-code-button .success{position:absolute;z-index:-100;top:50%;left:0;padding:.5em .65em;font-size:.825em;opacity:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.docsify-copy-code-button.error .error,.docsify-copy-code-button.success .success{opacity:1;-webkit-transform:translate(-115%,-50%);transform:translate(-115%,-50%)}.docsify-copy-code-button:focus,pre:hover .docsify-copy-code-button{opacity:1}"),document.querySelector('link[href*="docsify-copy-code"]')&&console.warn("[Deprecation] Link to external docsify-copy-code stylesheet is no longer necessary."),window.DocsifyCopyCodePlugin={init:function(){return function(o,e){o.ready(function(){console.warn("[Deprecation] Manually initializing docsify-copy-code using window.DocsifyCopyCodePlugin.init() is no longer necessary.")})}}},window.$docsify=window.$docsify||{},window.$docsify.plugins=[function(o,s){o.doneEach(function(){var o=Array.apply(null,document.querySelectorAll("pre[data-lang]")),c={buttonText:"Copy to clipboard",errorText:"Error",successText:"Copied"};s.config.copyCode&&Object.keys(c).forEach(function(t){var n=s.config.copyCode[t];"string"==typeof n?c[t]=n:"object"===r(n)&&Object.keys(n).some(function(o){var e=-1',''.concat(c.buttonText,""),''.concat(c.errorText,""),''.concat(c.successText,""),""].join("");o.forEach(function(o){o.insertAdjacentHTML("beforeend",e)})}),o.mounted(function(){document.querySelector(".content").addEventListener("click",function(o){if(o.target.classList.contains("docsify-copy-code-button")){var e="BUTTON"===o.target.tagName?o.target:o.target.parentNode,t=document.createRange(),n=e.parentNode.querySelector("code"),c=window.getSelection();t.selectNode(n),c.removeAllRanges(),c.addRange(t);try{document.execCommand("copy")&&(e.classList.add("success"),setTimeout(function(){e.classList.remove("success")},1e3))}catch(o){console.error("docsify-copy-code: ".concat(o)),e.classList.add("error"),setTimeout(function(){e.classList.remove("error")},1e3)}"function"==typeof(c=window.getSelection()).removeRange?c.removeRange(t):"function"==typeof c.removeAllRanges&&c.removeAllRanges()}})})}].concat(window.$docsify.plugins||[])}(); +//# sourceMappingURL=docsify-copy-code.min.js.map diff --git a/static/docsify.min.js b/static/docsify.min.js new file mode 100644 index 0000000..7893b85 --- /dev/null +++ b/static/docsify.min.js @@ -0,0 +1 @@ +!function(){function o(n){var r=Object.create(null);return function(e){var t=c(e)?e:JSON.stringify(e);return r[t]||(r[t]=n(e))}}var i=o(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),l=Object.prototype.hasOwnProperty,d=Object.assign||function(e){for(var t=arguments,n=1;n":">",'"':""","'":"'","/":"/"};return String(e).replace(/[&<>"'/]/g,function(e){return t[e]})}function p(e,t,r,i){void 0===i&&(i=h);var a=e._hooks[t],s=function(t){var e=a[t];if(t>=a.length)i(r);else if("function"==typeof e)if(2===e.length)e(r,function(e){r=e,s(t+1)});else{var n=e(r);r=void 0===n?r:n,s(t+1)}else s(t+1)};s(0)}var g=document.body.clientWidth<=600,a=window.history&&window.history.pushState&&window.history.replaceState&&!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/),n={};function m(e,t){if(void 0===t&&(t=!1),"string"==typeof e){if(void 0!==window.Vue)return y(e);e=t?y(e):n[e]||(n[e]=y(e))}return e}var f=document,v=f.body,b=f.head;function y(e,t){return t?e.querySelector(t):f.querySelector(e)}function k(e,t){return[].slice.call(t?e.querySelectorAll(t):f.querySelectorAll(e))}function w(e,t){return e=f.createElement(e),t&&(e.innerHTML=t),e}function s(e,t){return e.appendChild(t)}function x(e,t){return e.insertBefore(t,e.children[0])}function _(e,t,n){r(t)?window.addEventListener(e,t):e.addEventListener(t,n)}function S(e,t,n){r(t)?window.removeEventListener(e,t):e.removeEventListener(t,n)}function A(e,t,n){e&&e.classList[n?t:"toggle"](n||t)}var $,C,e=Object.freeze({__proto__:null,getNode:m,$:f,body:v,head:b,find:y,findAll:k,create:w,appendTo:s,before:x,on:_,off:S,toggleClass:A,style:function(e){s(b,w("style",e))}});function E(e,t){if(void 0===t&&(t='
    {inner}
'),!e||!e.length)return"";var n="";return e.forEach(function(e){n+='
  • '+e.title+"
  • ",e.children&&(n+=E(e.children,t))}),t.replace("{inner}",n)}function F(e,t){return'

    '+t.slice(5).trim()+"

    "}function L(e){var t,n=e.loaded,r=e.total,i=e.step;$||function(){var e=w("div");e.classList.add("progress"),s(v,e),$=e}(),t=i?80<(t=parseInt($.style.width||0,10)+i)?80:t:Math.floor(n/r*100),$.style.opacity=1,$.style.width=95<=t?"100%":t+"%",95<=t&&(clearTimeout(C),C=setTimeout(function(e){$.style.opacity=0,$.style.width="0%"},200))}var T={};function R(a,e,t){void 0===e&&(e=!1),void 0===t&&(t={});function n(){s.addEventListener.apply(s,arguments)}var s=new XMLHttpRequest,r=T[a];if(r)return{then:function(e){return e(r.content,r.opt)},abort:h};for(var i in s.open("GET",a),t)l.call(t,i)&&s.setRequestHeader(i,t[i]);return s.send(),{then:function(r,i){if(void 0===i&&(i=h),e){var t=setInterval(function(e){return L({step:Math.floor(5*Math.random()+1)})},500);n("progress",L),n("loadend",function(e){L(e),clearInterval(t)})}n("error",i),n("load",function(e){var t=e.target;if(400<=t.status)i(t);else{var n=T[a]={content:t.response,opt:{updatedAt:s.getResponseHeader("last-modified")}};r(n.content,n.opt)}})},abort:function(e){return 4!==s.readyState&&s.abort()}}}function O(e,t){e.innerHTML=e.innerHTML.replace(/var\(\s*--theme-color.*?\)/g,t)}var P=/([^{]*?)\w(?=\})/g,z={YYYY:"getFullYear",YY:"getYear",MM:function(e){return e.getMonth()+1},DD:"getDate",HH:"getHours",mm:"getMinutes",ss:"getSeconds",fff:"getMilliseconds"};var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function N(e,t){return e(t={exports:{}},t.exports),t.exports}var j=N(function(v,e){!function(){var y={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:/^ {0,3}(`{3,}|~{3,})([^`~\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|\\n*|\\n*|)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:h,table:h,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,text:/^[^\n]+/};function l(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||m.defaults,this.rules=y.normal,this.options.pedantic?this.rules=y.pedantic:this.options.gfm&&(this.rules=y.gfm)}y._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,y._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,y.def=e(y.def).replace("label",y._label).replace("title",y._title).getRegex(),y.bullet=/(?:[*+-]|\d{1,9}\.)/,y.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,y.item=e(y.item,"gm").replace(/bull/g,y.bullet).getRegex(),y.list=e(y.list).replace(/bull/g,y.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+y.def.source+")").getRegex(),y._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",y._comment=//,y.html=e(y.html,"i").replace("comment",y._comment).replace("tag",y._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),y.paragraph=e(y._paragraph).replace("hr",y.hr).replace("heading"," {0,3}#{1,6} +").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}|~{3,})[^`\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",y._tag).getRegex(),y.blockquote=e(y.blockquote).replace("paragraph",y.paragraph).getRegex(),y.normal=d({},y),y.gfm=d({},y.normal,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),y.pedantic=d({},y.normal,{html:e("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",y._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,fences:h,paragraph:e(y.normal._paragraph).replace("hr",y.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",y.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()}),l.rules=y,l.lex=function(e,t){return new l(t).lex(e)},l.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},l.prototype.token=function(e,t){var n,r,i,a,s,o,l,c,u,h,p,d,g,f,m,v;for(e=e.replace(/^ +$/gm,"");e;)if((i=this.rules.newline.exec(e))&&(e=e.substring(i[0].length),1 ?/gm,""),this.token(i,t),this.tokens.push({type:"blockquote_end"});else if(i=this.rules.list.exec(e)){for(e=e.substring(i[0].length),l={type:"list_start",ordered:f=1<(a=i[2]).length,start:f?+a:"",loose:!1},this.tokens.push(l),n=!(c=[]),g=(i=i[0].match(this.rules.item)).length,p=0;p?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:h,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,em:/^_([^\s_])_(?!_)|^\*([^\s*<\[])\*(?!\*)|^_([^\s<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s<"][\s\S]*?[^\s\*])\*(?!\*|[^\spunctuation])|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:h,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[^_{|}~",n.em=e(n.em).replace(/punctuation/g,n._punctuation).getRegex(),n._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,n._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,n._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,n.autolink=e(n.autolink).replace("scheme",n._scheme).replace("email",n._email).getRegex(),n._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,n.tag=e(n.tag).replace("comment",y._comment).replace("attribute",n._attribute).getRegex(),n._label=/(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,n._href=/<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/,n._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,n.link=e(n.link).replace("label",n._label).replace("href",n._href).replace("title",n._title).getRegex(),n.reflink=e(n.reflink).replace("label",n._label).getRegex(),n.normal=d({},n),n.pedantic=d({},n.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:e(/^!?\[(label)\]\((.*?)\)/).replace("label",n._label).getRegex(),reflink:e(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",n._label).getRegex()}),n.gfm=d({},n.normal,{escape:e(n.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/i.test(a[0])&&(this.inLink=!1),!this.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(a[0])?this.inRawBlock=!0:this.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(a[0])&&(this.inRawBlock=!1),e=e.substring(a[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0];else if(a=this.rules.link.exec(e)){var l=g(a[2],"()");if(-1$/,"$1"),o+=this.outputLink(a,{href:u.escapes(r),title:u.escapes(i)}),this.inLink=!1}else if((a=this.rules.reflink.exec(e))||(a=this.rules.nolink.exec(e))){if(e=e.substring(a[0].length),t=(a[2]||a[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){o+=a[0].charAt(0),e=a[0].substring(1)+e;continue}this.inLink=!0,o+=this.outputLink(a,t),this.inLink=!1}else if(a=this.rules.strong.exec(e))e=e.substring(a[0].length),o+=this.renderer.strong(this.output(a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.em.exec(e))e=e.substring(a[0].length),o+=this.renderer.em(this.output(a[6]||a[5]||a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.code.exec(e))e=e.substring(a[0].length),o+=this.renderer.codespan(k(a[2].trim(),!0));else if(a=this.rules.br.exec(e))e=e.substring(a[0].length),o+=this.renderer.br();else if(a=this.rules.del.exec(e))e=e.substring(a[0].length),o+=this.renderer.del(this.output(a[1]));else if(a=this.rules.autolink.exec(e))e=e.substring(a[0].length),r="@"===a[2]?"mailto:"+(n=k(this.mangle(a[1]))):n=k(a[1]),o+=this.renderer.link(r,null,n);else if(this.inLink||!(a=this.rules.url.exec(e))){if(a=this.rules.text.exec(e))e=e.substring(a[0].length),this.inRawBlock?o+=this.renderer.text(this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0]):o+=this.renderer.text(k(this.smartypants(a[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else{if("@"===a[2])r="mailto:"+(n=k(a[0]));else{for(;s=a[0],a[0]=this.rules._backpedal.exec(a[0])[0],s!==a[0];);n=k(a[0]),r="www."===a[1]?"http://"+n:n}e=e.substring(a[0].length),o+=this.renderer.link(r,null,n)}return o},u.escapes=function(e){return e?e.replace(u.rules._escapes,"$1"):e},u.prototype.outputLink=function(e,t){var n=t.href,r=t.title?k(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,k(e[1]))},u.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},u.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,i=0;i'+(n?e:k(e,!0))+"\n":"
    "+(n?e:k(e,!0))+"
    "},r.prototype.blockquote=function(e){return"
    \n"+e+"
    \n"},r.prototype.html=function(e){return e},r.prototype.heading=function(e,t,n,r){return this.options.headerIds?"'+e+"\n":""+e+"\n"},r.prototype.hr=function(){return this.options.xhtml?"
    \n":"
    \n"},r.prototype.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+"\n"},r.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},r.prototype.checkbox=function(e){return" "},r.prototype.paragraph=function(e){return"

    "+e+"

    \n"},r.prototype.table=function(e,t){return"
    \n\n"+e+"\n"+(t=t&&""+t+"")+"
    \n"},r.prototype.tablerow=function(e){return"\n"+e+"\n"},r.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+"\n"},r.prototype.strong=function(e){return""+e+""},r.prototype.em=function(e){return""+e+""},r.prototype.codespan=function(e){return""+e+""},r.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},r.prototype.del=function(e){return""+e+""},r.prototype.link=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"},r.prototype.image=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r=''+n+'":">"},r.prototype.text=function(e){return e},i.prototype.strong=i.prototype.em=i.prototype.codespan=i.prototype.del=i.prototype.text=function(e){return e},i.prototype.link=i.prototype.image=function(e,t,n){return""+n},i.prototype.br=function(){return""},c.parse=function(e,t){return new c(t).parse(e)},c.prototype.parse=function(e){this.inline=new u(e.links,this.options),this.inlineText=new u(e.links,d({},this.options,{renderer:new i})),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},c.prototype.next=function(){return this.token=this.tokens.pop(),this.token},c.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},c.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},c.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,p(this.inlineText.output(this.token.text)),this.slugger);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,i="",a="";for(n="",e=0;e?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t))for(var n=t;this.seen[n]++,t=n+"-"+this.seen[n],this.seen.hasOwnProperty(t););return this.seen[t]=0,t},k.escapeTest=/[&<>"']/,k.escapeReplace=/[&<>"']/g,k.replacements={"&":"&","<":"<",">":">",'"':""","'":"'"},k.escapeTestNoEncode=/[<>"']|&(?!#?\w+;)/,k.escapeReplaceNoEncode=/[<>"']|&(?!#?\w+;)/g;var s={},o=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function h(){}function d(e){for(var t,n,r=arguments,i=1;it)n.splice(t);else for(;n.lengthAn error occurred:

    "+k(e.message+"",!0)+"
    ";throw e}}h.exec=h,m.options=m.setOptions=function(e){return d(m.defaults,e),m},m.getDefaults=function(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:new r,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,xhtml:!1}},m.defaults=m.getDefaults(),m.Parser=c,m.parser=c.parse,m.Renderer=r,m.TextRenderer=i,m.Lexer=l,m.lexer=l.lex,m.InlineLexer=u,m.inlineLexer=u.output,m.Slugger=t,m.parse=m,v.exports=m}()}),M=N(function(e){var c=function(c){var u=/\blang(?:uage)?-([\w-]+)\b/i,t=0,T={manual:c.Prism&&c.Prism.manual,disableWorkerMessageHandler:c.Prism&&c.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof R?new R(e.type,T.util.encode(e.content),e.alias):Array.isArray(e)?e.map(T.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(y instanceof R)){if(d&&v!=t.length-1){if(u.lastIndex=b,!(A=u.exec(e)))break;for(var k=A.index+(p?A[1].length:0),w=A.index+A[0].length,x=v,_=b,S=t.length;x"+n.content+""},!c.document)return c.addEventListener&&(T.disableWorkerMessageHandler||c.addEventListener("message",function(e){var t=JSON.parse(e.data),n=t.language,r=t.code,i=t.immediateClose;c.postMessage(T.highlight(r,T.languages[n],n)),i&&c.close()},!1)),T;var e=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return e&&(T.filename=e.src,T.manual||e.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(T.highlightAll):window.setTimeout(T.highlightAll,16):document.addEventListener("DOMContentLoaded",T.highlightAll))),T}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=c),void 0!==t&&(t.Prism=c),c.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},c.languages.markup.tag.inside["attr-value"].inside.entity=c.languages.markup.entity,c.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(c.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:c.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:c.languages[t]};var i={};i[e]={pattern:RegExp(/(<__[\s\S]*?>)(?:\s*|[\s\S])*?(?=<\/__>)/.source.replace(/__/g,e),"i"),lookbehind:!0,greedy:!0,inside:r},c.languages.insertBefore("markup","cdata",i)}}),c.languages.xml=c.languages.extend("markup",{}),c.languages.html=c.languages.markup,c.languages.mathml=c.languages.markup,c.languages.svg=c.languages.markup,function(e){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/@[\w-]+/}},url:{pattern:RegExp("url\\((?:"+t.source+"|[^\n\r()]*)\\)","i"),inside:{function:/^url/i,punctuation:/^\(|\)$/}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+t.source+")*?(?=\\s*\\{)"),string:{pattern:t,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),e.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:e.languages.css}},alias:"language-css"}},n.tag))}(c),c.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},c.languages.javascript=c.languages.extend("clike",{"class-name":[c.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.])\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),c.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,c.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:c.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:c.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:c.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:c.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),c.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:c.languages.javascript}},string:/[\s\S]+/}}}),c.languages.markup&&c.languages.markup.tag.addInlined("script","javascript"),c.languages.js=c.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(e){e=e||document;var l={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(e.querySelectorAll("pre[data-src]")).forEach(function(e){if(!e.hasAttribute("data-src-loaded")){for(var t,n=e.getAttribute("data-src"),r=e,i=/\blang(?:uage)?-([\w-]+)\b/i;r&&!i.test(r.className);)r=r.parentNode;if(r&&(t=(e.className.match(i)||[,""])[1]),!t){var a=(n.match(/\.(\w+)$/)||[,""])[1];t=l[a]||a}var s=document.createElement("code");s.className="language-"+t,e.textContent="",s.textContent="Loading…",e.appendChild(s);var o=new XMLHttpRequest;o.open("GET",n,!0),o.onreadystatechange=function(){4==o.readyState&&(o.status<400&&o.responseText?(s.textContent=o.responseText,c.highlightElement(s),e.setAttribute("data-src-loaded","")):400<=o.status?s.textContent="✖ Error "+o.status+" while fetching file: "+o.statusText:s.textContent="✖ Error: File does not exist or is empty")},o.send(null)}}),c.plugins.toolbar&&c.plugins.toolbar.registerButton("download-file",function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-src")&&t.hasAttribute("data-download-link")){var n=t.getAttribute("data-src"),r=document.createElement("a");return r.textContent=t.getAttribute("data-download-link-label")||"Download",r.setAttribute("download",""),r.href=n,r}})},document.addEventListener("DOMContentLoaded",function(){self.Prism.fileHighlight()}))});function q(e,r){var i=[],a={};return e.forEach(function(e){var t=e.level||1,n=t-1;r?@[\]^`{|}~]/g;function Z(e){return e.toLowerCase()}function B(e){if("string"!=typeof e)return"";var t=e.trim().replace(/[A-Z]+/g,Z).replace(/<[^>\d]+>/g,"").replace(I,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),n=H[t];return n=l.call(H,t)?n+1:0,(H[t]=n)&&(t=t+"-"+n),t}function D(e,t){return''+t+''}B.clear=function(){H={}};var U=decodeURIComponent,Y=encodeURIComponent;function G(e){var n={};return(e=e.trim().replace(/^(\?|#|&)/,""))&&e.split("&").forEach(function(e){var t=e.replace(/\+/g," ").split("=");n[t[0]]=t[1]&&U(t[1])}),n}function V(e,t){void 0===t&&(t=[]);var n=[];for(var r in e)-1=m.length);n++){var r=t[n];if("string"==typeof r||r.content&&"string"==typeof r.content){var i=m[f],a=d.tokenStack[i],s="string"==typeof r?r:r.content,o=ne(g,i),l=s.indexOf(o);if(-1"}},video:function(e,t){return{html:'"}},audio:function(e,t){return{html:'"}},code:function(e,t){var n=e.match(/\.(\w+)$/);return"md"===(n=t||n&&n[1])&&(n="markdown"),{url:e,lang:n}}},se=function(i,e){var a=this;this.config=i,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=i.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?i.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var s,t=this._initRenderer(),n=i.markdown||{};s=r(n)?n(j,t):(j.setOptions(d(n,{renderer:d(t,n.renderer)})),j),this._marked=s,this.compile=function(n){var r=!0,e=o(function(e){r=!1;var t="";return n?(t=c(n)?s(n):s.parser(n),t=i.noEmoji?t:function(e){return e.replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(/:(\w+?):/gi,window.emojify||D).replace(/__colon__/g,":")}(t),B.clear(),t):n})(n),t=a.router.parse().file;return r?a.toc=a.cacheTOC[t]:a.cacheTOC[t]=[].concat(a.toc),e}};se.prototype.compileEmbed=function(e,t){var n,r=ie(t),i=r.str,a=r.config;if(t=i,a.include){var s;if(W(e)||(e=K(this.contentBase,X(this.router.getCurrentPath()),e)),a.type&&(s=ae[a.type]))(n=s.call(this,e,t)).type=a.type;else{var o="code";/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(n=ae[o].call(this,e,t)).type=o}return n.fragment=a.fragment,n}},se.prototype._matchNotCompileLink=function(e){for(var t=this.config.noCompileLinks||[],n=0;n
    '+r+""},t.code=e.code=function(e,t){return void 0===t&&(t=""),e=e.replace(/@DOCSIFY_QM@/g,"`"),'
    '+M.highlight(e,M.languages[t]||M.languages.markup)+"
    "},t.link=e.link=function(e,t,n){void 0===t&&(t="");var r="",i=ie(t),a=i.str,s=i.config;return t=a,W(e)||c._matchNotCompileLink(e)||s.ignore?(r+=0===e.indexOf("mailto:")?"":' target="'+o+'"',r+=0===e.indexOf("mailto:")?"":""!==l?' rel="'+l+'"':""):(e===c.config.homepage&&(e="README"),e=u.toURL(e,null,u.getCurrentPath())),s.target&&(r+=" target="+s.target),s.disabled&&(r+=" disabled",e="javascript:void(0)"),t&&(r+=' title="'+t+'"'),'"+n+""},t.paragraph=e.paragraph=function(e){return/^!>/.test(e)?F("tip",e):/^\?>/.test(e)?F("warn",e):"

    "+e+"

    "},t.image=e.image=function(e,t,n){var r=e,i="",a=ie(t),s=a.str,o=a.config;t=s,o["no-zoom"]&&(i+=" data-no-zoom"),t&&(i+=' title="'+t+'"');var l=o.size;if(l){var c=l.split("x");c[1]?i+="width="+c[0]+" height="+c[1]:i+="width="+c[0]}return W(e)||(r=K(h,X(u.getCurrentPath()),e)),''+n+'"},t.list=e.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+" "+[/
  • /.test(e.split('class="task-list"')[0])?'class="task-list"':"",n&&1"+e+""},t.listitem=e.listitem=function(e){return/^(]*>)/.test(e)?'
  • ":"
  • "+e+"
  • "},e.origin=t,e},se.prototype.sidebar=function(e,t){var n=this.toc,r=this.router.getCurrentPath(),i="";if(e)i=this.compile(e);else{for(var a=0;a{inner}"),this.cacheTree[r]=l}return i},se.prototype.subSidebar=function(e){if(e){var t=this.router.getCurrentPath(),n=this.cacheTree,r=this.toc;r[0]&&r[0].ignoreAllSubs&&r.splice(0),r[0]&&1===r[0].level&&r.shift();for(var i=0;i=t||e.classList.contains("hidden")?A(v,"add","sticky"):A(v,"remove","sticky")}}function ce(e,t,r,n){var i=[];null!=(t=m(t))&&(i=k(t,"a"));var a,s=decodeURI(e.toURL(e.getCurrentPath()));return i.sort(function(e,t){return t.href.length-e.href.length}).forEach(function(e){var t=e.getAttribute("href"),n=r?e.parentNode:e;0!==s.indexOf(t)||a?A(n,"remove","active"):(a=e,A(n,"add","active"))}),n&&(f.title=a?a.title||a.innerText+" - "+oe:oe),a}function ue(e,t){for(var n=0;nthis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,t,n,r){return(e/=r/2)<1?n/2*e*e+t:-n/2*(--e*(e-2)-1)+t}}]),fe);function fe(){var e=0o){t=t||u;break}t=u}if(t){var h=me[xe(decodeURIComponent(e),t.getAttribute("data-id"))];if(h&&h!==a&&(a&&a.classList.remove("active"),h.classList.add("active"),a=h,!ve&&v.classList.contains("sticky"))){var p=n.clientHeight,d=a.offsetTop+a.clientHeight+40,g=d-0=i.scrollTop&&d<=i.scrollTop+p?i.scrollTop:g?0:d-p;n.scrollTop=f}}}}function xe(e,t){return e+"?id="+t}function _e(e,t){if(t){var n=y("#"+t);n&&function(e){be&&be.stop(),ye=!1,be=new ge({start:window.pageYOffset,end:e.getBoundingClientRect().top+window.pageYOffset,duration:500}).on("tick",function(e){return window.scrollTo(0,e)}).on("done",function(){ye=!0,be=null}).begin()}(n);var r=me[xe(e,t)],i=y(m(".sidebar"),"li.active");i&&i.classList.remove("active"),r&&r.classList.add("active")}}var Se=f.scrollingElement||f.documentElement;var Ae={};function $e(e,i){var s=e.compiler,a=e.raw;void 0===a&&(a="");var t=e.fetch,n=Ae[a];if(n){var r=n.slice();return r.links=n.links,i(r)}var o=s._marked,l=o.lexer(a),c=[],u=o.InlineLexer.rules.link,h=l.links;l.forEach(function(e,a){"paragraph"===e.type&&(e.text=e.text.replace(new RegExp(u.source,"g"),function(e,t,n,r){var i=s.compileEmbed(n,r);return i&&c.push({index:a,embed:i}),e}))});var p=0;!function(e,a){var t,n=e.embedTokens,s=e.compile,o=(e.fetch,0),l=1;if(!n.length)return a({});for(;t=n[o++];){var r=function(i){return function(e){var t;if(e)if("markdown"===i.embed.type)t=s.lexer(e);else if("code"===i.embed.type){if(i.embed.fragment){var n=i.embed.fragment,r=new RegExp("(?:###|\\/\\/\\/)\\s*\\["+n+"\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\["+n+"\\]");e=((e.match(r)||[])[1]||"").trim()}t=s.lexer("```"+i.embed.lang+"\n"+e.replace(/`/g,"@DOCSIFY_QM@")+"\n```\n")}else"mermaid"===i.embed.type?(t=[{type:"html",text:'
    \n'+e+"\n
    "}]).links={}:(t=[{type:"html",text:e}]).links={};a({token:i,embedToken:t}),++l>=o&&a({})}}(t);t.embed.url?R(t.embed.url).then(r):r(t.embed.html)}}({compile:o,embedTokens:c,fetch:t},function(e){var t=e.embedToken,n=e.token;if(n){var r=n.index+p;d(h,t.links),l=l.slice(0,r).concat(t,l.slice(r+1)),p+=t.length-1}else Ae[a]=l.concat(),l.links=Ae[a].links=h,i(l)})}function Ce(){var e=k(".markdown-section>script").filter(function(e){return!/template/.test(e.type)})[0];if(!e)return!1;var t=e.innerText.trim();if(!t)return!1;setTimeout(function(e){window.__EXECUTE_RESULT__=new Function(t)()},0)}function Ee(e,t,n){return t="function"==typeof n?n(t):"string"==typeof n?function(r,i){var a=[],s=0;return r.replace(P,function(t,e,n){a.push(r.substring(s,n-1)),s=n+=t.length+1,a.push(i&&i[t]||function(e){return("00"+("string"==typeof z[t]?e[z[t]]():z[t](e))).slice(-t.length)})}),s!==r.length&&a.push(r.substring(s)),function(e){for(var t="",n=0,r=e||new Date;n404 - Not found",this._renderTo(".markdown-section",e),this.config.loadSidebar||this._renderSidebar(),!1===this.config.executeScript||void 0===window.Vue||Ce()?this.config.executeScript&&Ce():setTimeout(function(e){var t=window.__EXECUTE_RESULT__;t&&t.$destroy&&t.$destroy(),window.__EXECUTE_RESULT__=(new window.Vue).$mount("#main")},0)}function Le(e){var t=e.config;e.compiler=new se(t,e.router),window.__current_docsify_compiler__=e.compiler;var n=t.el||"#app",r=y("nav")||w("nav"),i=y(n),a="",s=v;if(i){if(t.repo&&(a+=function(e,t){return e?(/\/\//.test(e)||(e="https://github.com/"+e),''):""}(t.repo,t.cornerExternalLinkTarge)),t.coverpage&&(a+=function(){var e=", 100%, 85%";return'
    \x3c!--cover--\x3e
    '}()),t.logo){var o=/^data:image/.test(t.logo),l=/(?:http[s]?:)?\/\//.test(t.logo),c=/^\./.test(t.logo);o||l||c||(t.logo=K(e.router.getBasePath(),t.logo))}a+=function(e){var t=e.name?u(e.name):"",n='';return(g?n+"
    ":"
    "+n)+'
    \x3c!--main--\x3e
    '}(t),e._renderTo(i,a,!0)}else e.rendered=!0;t.mergeNavbar&&g?s=y(".sidebar"):(r.classList.add("app-nav"),t.repo||r.classList.add("no-badge")),t.loadNavbar&&x(s,r),t.themeColor&&(f.head.appendChild(w("div",function(e){return""}(t.themeColor)).firstElementChild),function(n){if(!(window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)"))){var e=k("style:not(.inserted),link");[].forEach.call(e,function(e){if("STYLE"===e.nodeName)O(e,n);else if("LINK"===e.nodeName){var t=e.getAttribute("href");if(!/\.css$/.test(t))return;R(t).then(function(e){var t=w("style",e);b.appendChild(t),O(t,n)})}})}}(t.themeColor)),e._updateRender(),A(v,"ready")}var Te={};function Re(e){this.config=e}function Oe(e){var t=location.href.indexOf("#");location.replace(location.href.slice(0,0<=t?t:0)+"#"+e)}Re.prototype.getBasePath=function(){return this.config.basePath},Re.prototype.getFile=function(e,t){void 0===e&&(e=this.getCurrentPath());var n=this.config,r=this.getBasePath(),i="string"==typeof n.ext?n.ext:".md";return e=(e=function(e,t){return new RegExp("\\.("+t.replace(/^\./,"")+"|html)$","g").test(e)?e:/\/$/g.test(e)?e+"README"+t:""+e+t}(e=n.alias?function e(t,n,r){var i=Object.keys(n).filter(function(e){return(Te[e]||(Te[e]=new RegExp("^"+e+"$"))).test(t)&&t!==r})[0];return i?e(t.replace(Te[i],n[i]),n,t):t}(e,n.alias):e,i))==="/README"+i&&n.homepage||e,e=W(e)?e:K(r,e),t&&(e=e.replace(new RegExp("^"+r),"")),e},Re.prototype.onchange=function(e){void 0===e&&(e=h),e()},Re.prototype.getCurrentPath=function(){},Re.prototype.normalize=function(){},Re.prototype.parse=function(){},Re.prototype.toURL=function(e,t,n){var r=n&&"#"===e[0],i=this.parse(te(e));if(i.query=d({},i.query,t),e=(e=i.path+V(i.query)).replace(/\.md(\?)|\.md$/,"$1"),r){var a=n.indexOf("?");e=(0([^<]*?)

    $');if(i){if("color"===i[2])n.style.background=i[1]+(i[3]||"");else{var a=i[1];A(n,"add","has-mask"),W(i[1])||(a=K(this.router.getBasePath(),i[1])),n.style.backgroundImage="url("http://github.com/+a+")",n.style.backgroundSize="cover",n.style.backgroundPosition="center center"}r=r.replace(i[0],"")}this._renderTo(".cover-main",r),le()}else A(n,"remove","show")},Ze._updateRender=function(){!function(e){var t=m(".app-name-link"),n=e.config.nameLink,r=e.route.path;if(t)if(c(e.config.nameLink))t.setAttribute("href",n);else if("object"==typeof n){var i=Object.keys(n).filter(function(e){return-1":">",'"':""","'":"'","/":"/"};return String(e).replace(/[&<>"'/]/g,function(e){return t[e]})}function p(e,t,r,i){void 0===i&&(i=h);var a=e._hooks[t],s=function(t){var e=a[t];if(t>=a.length)i(r);else if("function"==typeof e)if(2===e.length)e(r,function(e){r=e,s(t+1)});else{var n=e(r);r=void 0===n?r:n,s(t+1)}else s(t+1)};s(0)}var g=document.body.clientWidth<=600,a=window.history&&window.history.pushState&&window.history.replaceState&&!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/),n={};function m(e,t){if(void 0===t&&(t=!1),"string"==typeof e){if(void 0!==window.Vue)return y(e);e=t?y(e):n[e]||(n[e]=y(e))}return e}var f=document,v=f.body,b=f.head;function y(e,t){return t?e.querySelector(t):f.querySelector(e)}function k(e,t){return[].slice.call(t?e.querySelectorAll(t):f.querySelectorAll(e))}function w(e,t){return e=f.createElement(e),t&&(e.innerHTML=t),e}function s(e,t){return e.appendChild(t)}function x(e,t){return e.insertBefore(t,e.children[0])}function _(e,t,n){r(t)?window.addEventListener(e,t):e.addEventListener(t,n)}function S(e,t,n){r(t)?window.removeEventListener(e,t):e.removeEventListener(t,n)}function A(e,t,n){e&&e.classList[n?t:"toggle"](n||t)}var $,C,e=Object.freeze({__proto__:null,getNode:m,$:f,body:v,head:b,find:y,findAll:k,create:w,appendTo:s,before:x,on:_,off:S,toggleClass:A,style:function(e){s(b,w("style",e))}});function E(e,t){if(void 0===t&&(t='
      {inner}
    '),!e||!e.length)return"";var n="";return e.forEach(function(e){n+='
  • '+e.title+"
  • ",e.children&&(n+=E(e.children,t))}),t.replace("{inner}",n)}function F(e,t){return'

    '+t.slice(5).trim()+"

    "}function L(e){var t,n=e.loaded,r=e.total,i=e.step;$||function(){var e=w("div");e.classList.add("progress"),s(v,e),$=e}(),t=i?80<(t=parseInt($.style.width||0,10)+i)?80:t:Math.floor(n/r*100),$.style.opacity=1,$.style.width=95<=t?"100%":t+"%",95<=t&&(clearTimeout(C),C=setTimeout(function(e){$.style.opacity=0,$.style.width="0%"},200))}var T={};function R(a,e,t){void 0===e&&(e=!1),void 0===t&&(t={});function n(){s.addEventListener.apply(s,arguments)}var s=new XMLHttpRequest,r=T[a];if(r)return{then:function(e){return e(r.content,r.opt)},abort:h};for(var i in s.open("GET",a),t)l.call(t,i)&&s.setRequestHeader(i,t[i]);return s.send(),{then:function(r,i){if(void 0===i&&(i=h),e){var t=setInterval(function(e){return L({step:Math.floor(5*Math.random()+1)})},500);n("progress",L),n("loadend",function(e){L(e),clearInterval(t)})}n("error",i),n("load",function(e){var t=e.target;if(400<=t.status)i(t);else{var n=T[a]={content:t.response,opt:{updatedAt:s.getResponseHeader("last-modified")}};r(n.content,n.opt)}})},abort:function(e){return 4!==s.readyState&&s.abort()}}}function O(e,t){e.innerHTML=e.innerHTML.replace(/var\(\s*--theme-color.*?\)/g,t)}var P=/([^{]*?)\w(?=\})/g,z={YYYY:"getFullYear",YY:"getYear",MM:function(e){return e.getMonth()+1},DD:"getDate",HH:"getHours",mm:"getMinutes",ss:"getSeconds",fff:"getMilliseconds"};var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function N(e,t){return e(t={exports:{}},t.exports),t.exports}var j=N(function(v,e){!function(){var y={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:/^ {0,3}(`{3,}|~{3,})([^`~\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|\\n*|\\n*|)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:h,table:h,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,text:/^[^\n]+/};function l(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||m.defaults,this.rules=y.normal,this.options.pedantic?this.rules=y.pedantic:this.options.gfm&&(this.rules=y.gfm)}y._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,y._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,y.def=e(y.def).replace("label",y._label).replace("title",y._title).getRegex(),y.bullet=/(?:[*+-]|\d{1,9}\.)/,y.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,y.item=e(y.item,"gm").replace(/bull/g,y.bullet).getRegex(),y.list=e(y.list).replace(/bull/g,y.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+y.def.source+")").getRegex(),y._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",y._comment=//,y.html=e(y.html,"i").replace("comment",y._comment).replace("tag",y._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),y.paragraph=e(y._paragraph).replace("hr",y.hr).replace("heading"," {0,3}#{1,6} +").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}|~{3,})[^`\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",y._tag).getRegex(),y.blockquote=e(y.blockquote).replace("paragraph",y.paragraph).getRegex(),y.normal=d({},y),y.gfm=d({},y.normal,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),y.pedantic=d({},y.normal,{html:e("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",y._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,fences:h,paragraph:e(y.normal._paragraph).replace("hr",y.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",y.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()}),l.rules=y,l.lex=function(e,t){return new l(t).lex(e)},l.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},l.prototype.token=function(e,t){var n,r,i,a,s,o,l,c,u,h,p,d,g,f,m,v;for(e=e.replace(/^ +$/gm,"");e;)if((i=this.rules.newline.exec(e))&&(e=e.substring(i[0].length),1 ?/gm,""),this.token(i,t),this.tokens.push({type:"blockquote_end"});else if(i=this.rules.list.exec(e)){for(e=e.substring(i[0].length),l={type:"list_start",ordered:f=1<(a=i[2]).length,start:f?+a:"",loose:!1},this.tokens.push(l),n=!(c=[]),g=(i=i[0].match(this.rules.item)).length,p=0;p?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:h,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,em:/^_([^\s_])_(?!_)|^\*([^\s*<\[])\*(?!\*)|^_([^\s<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s<"][\s\S]*?[^\s\*])\*(?!\*|[^\spunctuation])|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:h,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[^_{|}~",n.em=e(n.em).replace(/punctuation/g,n._punctuation).getRegex(),n._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,n._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,n._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,n.autolink=e(n.autolink).replace("scheme",n._scheme).replace("email",n._email).getRegex(),n._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,n.tag=e(n.tag).replace("comment",y._comment).replace("attribute",n._attribute).getRegex(),n._label=/(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,n._href=/<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/,n._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,n.link=e(n.link).replace("label",n._label).replace("href",n._href).replace("title",n._title).getRegex(),n.reflink=e(n.reflink).replace("label",n._label).getRegex(),n.normal=d({},n),n.pedantic=d({},n.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:e(/^!?\[(label)\]\((.*?)\)/).replace("label",n._label).getRegex(),reflink:e(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",n._label).getRegex()}),n.gfm=d({},n.normal,{escape:e(n.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/i.test(a[0])&&(this.inLink=!1),!this.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(a[0])?this.inRawBlock=!0:this.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(a[0])&&(this.inRawBlock=!1),e=e.substring(a[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0];else if(a=this.rules.link.exec(e)){var l=g(a[2],"()");if(-1$/,"$1"),o+=this.outputLink(a,{href:u.escapes(r),title:u.escapes(i)}),this.inLink=!1}else if((a=this.rules.reflink.exec(e))||(a=this.rules.nolink.exec(e))){if(e=e.substring(a[0].length),t=(a[2]||a[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){o+=a[0].charAt(0),e=a[0].substring(1)+e;continue}this.inLink=!0,o+=this.outputLink(a,t),this.inLink=!1}else if(a=this.rules.strong.exec(e))e=e.substring(a[0].length),o+=this.renderer.strong(this.output(a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.em.exec(e))e=e.substring(a[0].length),o+=this.renderer.em(this.output(a[6]||a[5]||a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.code.exec(e))e=e.substring(a[0].length),o+=this.renderer.codespan(k(a[2].trim(),!0));else if(a=this.rules.br.exec(e))e=e.substring(a[0].length),o+=this.renderer.br();else if(a=this.rules.del.exec(e))e=e.substring(a[0].length),o+=this.renderer.del(this.output(a[1]));else if(a=this.rules.autolink.exec(e))e=e.substring(a[0].length),r="@"===a[2]?"mailto:"+(n=k(this.mangle(a[1]))):n=k(a[1]),o+=this.renderer.link(r,null,n);else if(this.inLink||!(a=this.rules.url.exec(e))){if(a=this.rules.text.exec(e))e=e.substring(a[0].length),this.inRawBlock?o+=this.renderer.text(this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0]):o+=this.renderer.text(k(this.smartypants(a[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else{if("@"===a[2])r="mailto:"+(n=k(a[0]));else{for(;s=a[0],a[0]=this.rules._backpedal.exec(a[0])[0],s!==a[0];);n=k(a[0]),r="www."===a[1]?"http://"+n:n}e=e.substring(a[0].length),o+=this.renderer.link(r,null,n)}return o},u.escapes=function(e){return e?e.replace(u.rules._escapes,"$1"):e},u.prototype.outputLink=function(e,t){var n=t.href,r=t.title?k(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,k(e[1]))},u.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},u.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,i=0;i'+(n?e:k(e,!0))+"\n":"
    "+(n?e:k(e,!0))+"
    "},r.prototype.blockquote=function(e){return"
    \n"+e+"
    \n"},r.prototype.html=function(e){return e},r.prototype.heading=function(e,t,n,r){return this.options.headerIds?"'+e+"\n":""+e+"\n"},r.prototype.hr=function(){return this.options.xhtml?"
    \n":"
    \n"},r.prototype.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+"\n"},r.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},r.prototype.checkbox=function(e){return" "},r.prototype.paragraph=function(e){return"

    "+e+"

    \n"},r.prototype.table=function(e,t){return"\n\n"+e+"\n"+(t=t&&""+t+"")+"
    \n"},r.prototype.tablerow=function(e){return"\n"+e+"\n"},r.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+"\n"},r.prototype.strong=function(e){return""+e+""},r.prototype.em=function(e){return""+e+""},r.prototype.codespan=function(e){return""+e+""},r.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},r.prototype.del=function(e){return""+e+""},r.prototype.link=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"},r.prototype.image=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r=''+n+'":">"},r.prototype.text=function(e){return e},i.prototype.strong=i.prototype.em=i.prototype.codespan=i.prototype.del=i.prototype.text=function(e){return e},i.prototype.link=i.prototype.image=function(e,t,n){return""+n},i.prototype.br=function(){return""},c.parse=function(e,t){return new c(t).parse(e)},c.prototype.parse=function(e){this.inline=new u(e.links,this.options),this.inlineText=new u(e.links,d({},this.options,{renderer:new i})),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},c.prototype.next=function(){return this.token=this.tokens.pop(),this.token},c.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},c.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},c.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,p(this.inlineText.output(this.token.text)),this.slugger);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,i="",a="";for(n="",e=0;e?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t))for(var n=t;this.seen[n]++,t=n+"-"+this.seen[n],this.seen.hasOwnProperty(t););return this.seen[t]=0,t},k.escapeTest=/[&<>"']/,k.escapeReplace=/[&<>"']/g,k.replacements={"&":"&","<":"<",">":">",'"':""","'":"'"},k.escapeTestNoEncode=/[<>"']|&(?!#?\w+;)/,k.escapeReplaceNoEncode=/[<>"']|&(?!#?\w+;)/g;var s={},o=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function h(){}function d(e){for(var t,n,r=arguments,i=1;it)n.splice(t);else for(;n.lengthAn error occurred:

    "+k(e.message+"",!0)+"
    ";throw e}}h.exec=h,m.options=m.setOptions=function(e){return d(m.defaults,e),m},m.getDefaults=function(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:new r,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,xhtml:!1}},m.defaults=m.getDefaults(),m.Parser=c,m.parser=c.parse,m.Renderer=r,m.TextRenderer=i,m.Lexer=l,m.lexer=l.lex,m.InlineLexer=u,m.inlineLexer=u.output,m.Slugger=t,m.parse=m,v.exports=m}()}),M=N(function(e){var c=function(c){var u=/\blang(?:uage)?-([\w-]+)\b/i,t=0,T={manual:c.Prism&&c.Prism.manual,disableWorkerMessageHandler:c.Prism&&c.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof R?new R(e.type,T.util.encode(e.content),e.alias):Array.isArray(e)?e.map(T.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(y instanceof R)){if(d&&v!=t.length-1){if(u.lastIndex=b,!(A=u.exec(e)))break;for(var k=A.index+(p?A[1].length:0),w=A.index+A[0].length,x=v,_=b,S=t.length;x"+n.content+""},!c.document)return c.addEventListener&&(T.disableWorkerMessageHandler||c.addEventListener("message",function(e){var t=JSON.parse(e.data),n=t.language,r=t.code,i=t.immediateClose;c.postMessage(T.highlight(r,T.languages[n],n)),i&&c.close()},!1)),T;var e=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return e&&(T.filename=e.src,T.manual||e.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(T.highlightAll):window.setTimeout(T.highlightAll,16):document.addEventListener("DOMContentLoaded",T.highlightAll))),T}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=c),void 0!==t&&(t.Prism=c),c.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},c.languages.markup.tag.inside["attr-value"].inside.entity=c.languages.markup.entity,c.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(c.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:c.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:c.languages[t]};var i={};i[e]={pattern:RegExp(/(<__[\s\S]*?>)(?:\s*|[\s\S])*?(?=<\/__>)/.source.replace(/__/g,e),"i"),lookbehind:!0,greedy:!0,inside:r},c.languages.insertBefore("markup","cdata",i)}}),c.languages.xml=c.languages.extend("markup",{}),c.languages.html=c.languages.markup,c.languages.mathml=c.languages.markup,c.languages.svg=c.languages.markup,function(e){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/@[\w-]+/}},url:{pattern:RegExp("url\\((?:"+t.source+"|[^\n\r()]*)\\)","i"),inside:{function:/^url/i,punctuation:/^\(|\)$/}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+t.source+")*?(?=\\s*\\{)"),string:{pattern:t,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),e.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:e.languages.css}},alias:"language-css"}},n.tag))}(c),c.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},c.languages.javascript=c.languages.extend("clike",{"class-name":[c.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.])\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),c.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,c.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:c.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:c.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:c.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:c.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),c.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:c.languages.javascript}},string:/[\s\S]+/}}}),c.languages.markup&&c.languages.markup.tag.addInlined("script","javascript"),c.languages.js=c.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(e){e=e||document;var l={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(e.querySelectorAll("pre[data-src]")).forEach(function(e){if(!e.hasAttribute("data-src-loaded")){for(var t,n=e.getAttribute("data-src"),r=e,i=/\blang(?:uage)?-([\w-]+)\b/i;r&&!i.test(r.className);)r=r.parentNode;if(r&&(t=(e.className.match(i)||[,""])[1]),!t){var a=(n.match(/\.(\w+)$/)||[,""])[1];t=l[a]||a}var s=document.createElement("code");s.className="language-"+t,e.textContent="",s.textContent="Loading…",e.appendChild(s);var o=new XMLHttpRequest;o.open("GET",n,!0),o.onreadystatechange=function(){4==o.readyState&&(o.status<400&&o.responseText?(s.textContent=o.responseText,c.highlightElement(s),e.setAttribute("data-src-loaded","")):400<=o.status?s.textContent="✖ Error "+o.status+" while fetching file: "+o.statusText:s.textContent="✖ Error: File does not exist or is empty")},o.send(null)}}),c.plugins.toolbar&&c.plugins.toolbar.registerButton("download-file",function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-src")&&t.hasAttribute("data-download-link")){var n=t.getAttribute("data-src"),r=document.createElement("a");return r.textContent=t.getAttribute("data-download-link-label")||"Download",r.setAttribute("download",""),r.href=n,r}})},document.addEventListener("DOMContentLoaded",function(){self.Prism.fileHighlight()}))});function q(e,r){var i=[],a={};return e.forEach(function(e){var t=e.level||1,n=t-1;r?@[\]^`{|}~]/g;function Z(e){return e.toLowerCase()}function B(e){if("string"!=typeof e)return"";var t=e.trim().replace(/[A-Z]+/g,Z).replace(/<[^>\d]+>/g,"").replace(I,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),n=H[t];return n=l.call(H,t)?n+1:0,(H[t]=n)&&(t=t+"-"+n),t}function D(e,t){return''+t+''}B.clear=function(){H={}};var U=decodeURIComponent,Y=encodeURIComponent;function G(e){var n={};return(e=e.trim().replace(/^(\?|#|&)/,""))&&e.split("&").forEach(function(e){var t=e.replace(/\+/g," ").split("=");n[t[0]]=t[1]&&U(t[1])}),n}function V(e,t){void 0===t&&(t=[]);var n=[];for(var r in e)-1=m.length);n++){var r=t[n];if("string"==typeof r||r.content&&"string"==typeof r.content){var i=m[f],a=d.tokenStack[i],s="string"==typeof r?r:r.content,o=ne(g,i),l=s.indexOf(o);if(-1"}},video:function(e,t){return{html:'"}},audio:function(e,t){return{html:'"}},code:function(e,t){var n=e.match(/\.(\w+)$/);return"md"===(n=t||n&&n[1])&&(n="markdown"),{url:e,lang:n}}},se=function(i,e){var a=this;this.config=i,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=i.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?i.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var s,t=this._initRenderer(),n=i.markdown||{};s=r(n)?n(j,t):(j.setOptions(d(n,{renderer:d(t,n.renderer)})),j),this._marked=s,this.compile=function(n){var r=!0,e=o(function(e){r=!1;var t="";return n?(t=c(n)?s(n):s.parser(n),t=i.noEmoji?t:function(e){return e.replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(/:(\w+?):/gi,window.emojify||D).replace(/__colon__/g,":")}(t),B.clear(),t):n})(n),t=a.router.parse().file;return r?a.toc=a.cacheTOC[t]:a.cacheTOC[t]=[].concat(a.toc),e}};se.prototype.compileEmbed=function(e,t){var n,r=ie(t),i=r.str,a=r.config;if(t=i,a.include){var s;if(W(e)||(e=K(this.contentBase,X(this.router.getCurrentPath()),e)),a.type&&(s=ae[a.type]))(n=s.call(this,e,t)).type=a.type;else{var o="code";/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(n=ae[o].call(this,e,t)).type=o}return n.fragment=a.fragment,n}},se.prototype._matchNotCompileLink=function(e){for(var t=this.config.noCompileLinks||[],n=0;n
    '+r+""},t.code=e.code=function(e,t){return void 0===t&&(t=""),e=e.replace(/@DOCSIFY_QM@/g,"`"),'
    '+M.highlight(e,M.languages[t]||M.languages.markup)+"
    "},t.link=e.link=function(e,t,n){void 0===t&&(t="");var r="",i=ie(t),a=i.str,s=i.config;return t=a,W(e)||c._matchNotCompileLink(e)||s.ignore?(r+=0===e.indexOf("mailto:")?"":' target="'+o+'"',r+=0===e.indexOf("mailto:")?"":""!==l?' rel="'+l+'"':""):(e===c.config.homepage&&(e="README"),e=u.toURL(e,null,u.getCurrentPath())),s.target&&(r+=" target="+s.target),s.disabled&&(r+=" disabled",e="javascript:void(0)"),t&&(r+=' title="'+t+'"'),'"+n+""},t.paragraph=e.paragraph=function(e){return/^!>/.test(e)?F("tip",e):/^\?>/.test(e)?F("warn",e):"

    "+e+"

    "},t.image=e.image=function(e,t,n){var r=e,i="",a=ie(t),s=a.str,o=a.config;t=s,o["no-zoom"]&&(i+=" data-no-zoom"),t&&(i+=' title="'+t+'"');var l=o.size;if(l){var c=l.split("x");c[1]?i+="width="+c[0]+" height="+c[1]:i+="width="+c[0]}return W(e)||(r=K(h,X(u.getCurrentPath()),e)),''+n+'"},t.list=e.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+" "+[/
  • /.test(e.split('class="task-list"')[0])?'class="task-list"':"",n&&1"+e+""},t.listitem=e.listitem=function(e){return/^(]*>)/.test(e)?'
  • ":"
  • "+e+"
  • "},e.origin=t,e},se.prototype.sidebar=function(e,t){var n=this.toc,r=this.router.getCurrentPath(),i="";if(e)i=this.compile(e);else{for(var a=0;a{inner}"),this.cacheTree[r]=l}return i},se.prototype.subSidebar=function(e){if(e){var t=this.router.getCurrentPath(),n=this.cacheTree,r=this.toc;r[0]&&r[0].ignoreAllSubs&&r.splice(0),r[0]&&1===r[0].level&&r.shift();for(var i=0;i=t||e.classList.contains("hidden")?A(v,"add","sticky"):A(v,"remove","sticky")}}function ce(e,t,r,n){var i=[];null!=(t=m(t))&&(i=k(t,"a"));var a,s=decodeURI(e.toURL(e.getCurrentPath()));return i.sort(function(e,t){return t.href.length-e.href.length}).forEach(function(e){var t=e.getAttribute("href"),n=r?e.parentNode:e;0!==s.indexOf(t)||a?A(n,"remove","active"):(a=e,A(n,"add","active"))}),n&&(f.title=a?a.title||a.innerText+" - "+oe:oe),a}function ue(e,t){for(var n=0;nthis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,t,n,r){return(e/=r/2)<1?n/2*e*e+t:-n/2*(--e*(e-2)-1)+t}}]),fe);function fe(){var e=0o){t=t||u;break}t=u}if(t){var h=me[xe(decodeURIComponent(e),t.getAttribute("data-id"))];if(h&&h!==a&&(a&&a.classList.remove("active"),h.classList.add("active"),a=h,!ve&&v.classList.contains("sticky"))){var p=n.clientHeight,d=a.offsetTop+a.clientHeight+40,g=d-0=i.scrollTop&&d<=i.scrollTop+p?i.scrollTop:g?0:d-p;n.scrollTop=f}}}}function xe(e,t){return e+"?id="+t}function _e(e,t){if(t){var n=y("#"+t);n&&function(e){be&&be.stop(),ye=!1,be=new ge({start:window.pageYOffset,end:e.getBoundingClientRect().top+window.pageYOffset,duration:500}).on("tick",function(e){return window.scrollTo(0,e)}).on("done",function(){ye=!0,be=null}).begin()}(n);var r=me[xe(e,t)],i=y(m(".sidebar"),"li.active");i&&i.classList.remove("active"),r&&r.classList.add("active")}}var Se=f.scrollingElement||f.documentElement;var Ae={};function $e(e,i){var s=e.compiler,a=e.raw;void 0===a&&(a="");var t=e.fetch,n=Ae[a];if(n){var r=n.slice();return r.links=n.links,i(r)}var o=s._marked,l=o.lexer(a),c=[],u=o.InlineLexer.rules.link,h=l.links;l.forEach(function(e,a){"paragraph"===e.type&&(e.text=e.text.replace(new RegExp(u.source,"g"),function(e,t,n,r){var i=s.compileEmbed(n,r);return i&&c.push({index:a,embed:i}),e}))});var p=0;!function(e,a){var t,n=e.embedTokens,s=e.compile,o=(e.fetch,0),l=1;if(!n.length)return a({});for(;t=n[o++];){var r=function(i){return function(e){var t;if(e)if("markdown"===i.embed.type)t=s.lexer(e);else if("code"===i.embed.type){if(i.embed.fragment){var n=i.embed.fragment,r=new RegExp("(?:###|\\/\\/\\/)\\s*\\["+n+"\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\["+n+"\\]");e=((e.match(r)||[])[1]||"").trim()}t=s.lexer("```"+i.embed.lang+"\n"+e.replace(/`/g,"@DOCSIFY_QM@")+"\n```\n")}else"mermaid"===i.embed.type?(t=[{type:"html",text:'
    \n'+e+"\n
    "}]).links={}:(t=[{type:"html",text:e}]).links={};a({token:i,embedToken:t}),++l>=o&&a({})}}(t);t.embed.url?R(t.embed.url).then(r):r(t.embed.html)}}({compile:o,embedTokens:c,fetch:t},function(e){var t=e.embedToken,n=e.token;if(n){var r=n.index+p;d(h,t.links),l=l.slice(0,r).concat(t,l.slice(r+1)),p+=t.length-1}else Ae[a]=l.concat(),l.links=Ae[a].links=h,i(l)})}function Ce(){var e=k(".markdown-section>script").filter(function(e){return!/template/.test(e.type)})[0];if(!e)return!1;var t=e.innerText.trim();if(!t)return!1;setTimeout(function(e){window.__EXECUTE_RESULT__=new Function(t)()},0)}function Ee(e,t,n){return t="function"==typeof n?n(t):"string"==typeof n?function(r,i){var a=[],s=0;return r.replace(P,function(t,e,n){a.push(r.substring(s,n-1)),s=n+=t.length+1,a.push(i&&i[t]||function(e){return("00"+("string"==typeof z[t]?e[z[t]]():z[t](e))).slice(-t.length)})}),s!==r.length&&a.push(r.substring(s)),function(e){for(var t="",n=0,r=e||new Date;n404 - Not found",this._renderTo(".markdown-section",e),this.config.loadSidebar||this._renderSidebar(),!1===this.config.executeScript||void 0===window.Vue||Ce()?this.config.executeScript&&Ce():setTimeout(function(e){var t=window.__EXECUTE_RESULT__;t&&t.$destroy&&t.$destroy(),window.__EXECUTE_RESULT__=(new window.Vue).$mount("#main")},0)}function Le(e){var t=e.config;e.compiler=new se(t,e.router),window.__current_docsify_compiler__=e.compiler;var n=t.el||"#app",r=y("nav")||w("nav"),i=y(n),a="",s=v;if(i){if(t.repo&&(a+=function(e,t){return e?(/\/\//.test(e)||(e="https://github.com/"+e),''):""}(t.repo,t.cornerExternalLinkTarge)),t.coverpage&&(a+=function(){var e=", 100%, 85%";return'
    \x3c!--cover--\x3e
    '}()),t.logo){var o=/^data:image/.test(t.logo),l=/(?:http[s]?:)?\/\//.test(t.logo),c=/^\./.test(t.logo);o||l||c||(t.logo=K(e.router.getBasePath(),t.logo))}a+=function(e){var t=e.name?u(e.name):"",n='';return(g?n+"
    ":"
    "+n)+'
    \x3c!--main--\x3e
    '}(t),e._renderTo(i,a,!0)}else e.rendered=!0;t.mergeNavbar&&g?s=y(".sidebar"):(r.classList.add("app-nav"),t.repo||r.classList.add("no-badge")),t.loadNavbar&&x(s,r),t.themeColor&&(f.head.appendChild(w("div",function(e){return""}(t.themeColor)).firstElementChild),function(n){if(!(window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)"))){var e=k("style:not(.inserted),link");[].forEach.call(e,function(e){if("STYLE"===e.nodeName)O(e,n);else if("LINK"===e.nodeName){var t=e.getAttribute("href");if(!/\.css$/.test(t))return;R(t).then(function(e){var t=w("style",e);b.appendChild(t),O(t,n)})}})}}(t.themeColor)),e._updateRender(),A(v,"ready")}var Te={};function Re(e){this.config=e}function Oe(e){var t=location.href.indexOf("#");location.replace(location.href.slice(0,0<=t?t:0)+"#"+e)}Re.prototype.getBasePath=function(){return this.config.basePath},Re.prototype.getFile=function(e,t){void 0===e&&(e=this.getCurrentPath());var n=this.config,r=this.getBasePath(),i="string"==typeof n.ext?n.ext:".md";return e=(e=function(e,t){return new RegExp("\\.("+t.replace(/^\./,"")+"|html)$","g").test(e)?e:/\/$/g.test(e)?e+"README"+t:""+e+t}(e=n.alias?function e(t,n,r){var i=Object.keys(n).filter(function(e){return(Te[e]||(Te[e]=new RegExp("^"+e+"$"))).test(t)&&t!==r})[0];return i?e(t.replace(Te[i],n[i]),n,t):t}(e,n.alias):e,i))==="/README"+i&&n.homepage||e,e=W(e)?e:K(r,e),t&&(e=e.replace(new RegExp("^"+r),"")),e},Re.prototype.onchange=function(e){void 0===e&&(e=h),e()},Re.prototype.getCurrentPath=function(){},Re.prototype.normalize=function(){},Re.prototype.parse=function(){},Re.prototype.toURL=function(e,t,n){var r=n&&"#"===e[0],i=this.parse(te(e));if(i.query=d({},i.query,t),e=(e=i.path+V(i.query)).replace(/\.md(\?)|\.md$/,"$1"),r){var a=n.indexOf("?");e=(0([^<]*?)

    $');if(i){if("color"===i[2])n.style.background=i[1]+(i[3]||"");else{var a=i[1];A(n,"add","has-mask"),W(i[1])||(a=K(this.router.getBasePath(),i[1])),n.style.backgroundImage="url("http://github.com/+a+")",n.style.backgroundSize="cover",n.style.backgroundPosition="center center"}r=r.replace(i[0],"")}this._renderTo(".cover-main",r),le()}else A(n,"remove","show")},Ze._updateRender=function(){!function(e){var t=m(".app-name-link"),n=e.config.nameLink,r=e.route.path;if(t)if(c(e.config.nameLink))t.setAttribute("href",n);else if("object"==typeof n){var i=Object.keys(n).filter(function(e){return-1 + + + Class Loader + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Class Loader Subsystem + + + + + + + + + + + + + + + + Extension ClassLoader + + + + Application ClassLoader + + + Bootstrap ClassLoader + + + Loading + + + + + + + + + + + + + Prepare + + + + Resolve + + + Verify + + + Linking + + + + + + + + + + Init + + + Initialization + + + + + + + + + + \ No newline at end of file diff --git a/static/minpath.jpg b/static/minpath.jpg new file mode 100644 index 0000000..301be95 Binary files /dev/null and b/static/minpath.jpg differ diff --git a/static/o_update1.png b/static/o_update1.png new file mode 100644 index 0000000..28aa994 Binary files /dev/null and b/static/o_update1.png differ diff --git a/static/o_update2-20200419221055438.png b/static/o_update2-20200419221055438.png new file mode 100644 index 0000000..fbb076e Binary files /dev/null and b/static/o_update2-20200419221055438.png differ diff --git a/static/o_update2.png b/static/o_update2.png new file mode 100644 index 0000000..fbb076e Binary files /dev/null and b/static/o_update2.png differ diff --git a/static/prism-java.js b/static/prism-java.js new file mode 100644 index 0000000..6433162 --- /dev/null +++ b/static/prism-java.js @@ -0,0 +1,54 @@ +(function (Prism) { + + var keywords = /\b(?:abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while|var|null|exports|module|open|opens|provides|requires|to|transitive|uses|with)\b/; + + // based on the java naming conventions + var className = /\b[A-Z](?:\w*[a-z]\w*)?\b/; + + Prism.languages.java = Prism.languages.extend('clike', { + 'class-name': [ + className, + + // variables and parameters + // this to support class names (or generic parameters) which do not contain a lower case letter (also works for methods) + /\b[A-Z]\w*(?=\s+\w+\s*[;,=())])/ + ], + 'keyword': keywords, + 'function': [ + Prism.languages.clike.function, + { + pattern: /(\:\:)[a-z_]\w*/, + lookbehind: true + } + ], + 'number': /\b0b[01][01_]*L?\b|\b0x[\da-f_]*\.?[\da-f_p+-]+\b|(?:\b\d[\d_]*\.?[\d_]*|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i, + 'operator': { + pattern: /(^|[^.])(?:<<=?|>>>?=?|->|([-+&|])\2|[?:~]|[-+*/%&|^!=<>]=?)/m, + lookbehind: true + } + }); + + Prism.languages.insertBefore('java', 'class-name', { + 'annotation': { + alias: 'punctuation', + pattern: /(^|[^.])@\w+/, + lookbehind: true + }, + 'namespace': { + pattern: /(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)[a-z]\w*(\.[a-z]\w*)+/, + lookbehind: true, + inside: { + 'punctuation': /\./, + } + }, + 'generics': { + pattern: /<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/, + inside: { + 'class-name': className, + 'keyword': keywords, + 'punctuation': /[<>(),.:]/, + 'operator': /[?&|]/ + } + } + }); +}(Prism)); diff --git a/static/qe222wewewqere.jpeg b/static/qe222wewewqere.jpeg new file mode 100644 index 0000000..6c10fbc Binary files /dev/null and b/static/qe222wewewqere.jpeg differ diff --git a/static/question_11.jpg b/static/question_11.jpg new file mode 100644 index 0000000..7661efe Binary files /dev/null and b/static/question_11.jpg differ diff --git a/static/rainwatertrap.png b/static/rainwatertrap.png new file mode 100644 index 0000000..578e81e Binary files /dev/null and b/static/rainwatertrap.png differ diff --git a/static/reprint.png b/static/reprint.png new file mode 100644 index 0000000..89dab3f Binary files /dev/null and b/static/reprint.png differ diff --git a/static/robot_maze.png b/static/robot_maze.png new file mode 100644 index 0000000..ebcd2dc Binary files /dev/null and b/static/robot_maze.png differ diff --git a/static/s27297117.jpg b/static/s27297117.jpg new file mode 100644 index 0000000..7454069 Binary files /dev/null and b/static/s27297117.jpg differ diff --git a/static/s29063065.jpg b/static/s29063065.jpg new file mode 100644 index 0000000..7e56fb3 Binary files /dev/null and b/static/s29063065.jpg differ diff --git a/static/s32282160.jpg b/static/s32282160.jpg new file mode 100755 index 0000000..d8df0c4 Binary files /dev/null and b/static/s32282160.jpg differ diff --git a/static/s32306279.jpg b/static/s32306279.jpg new file mode 100644 index 0000000..d6bb6bf Binary files /dev/null and b/static/s32306279.jpg differ diff --git a/static/s32332106.jpg b/static/s32332106.jpg new file mode 100755 index 0000000..d4a6616 Binary files /dev/null and b/static/s32332106.jpg differ diff --git a/static/s33531736.jpg b/static/s33531736.jpg new file mode 100644 index 0000000..6718f6b Binary files /dev/null and b/static/s33531736.jpg differ diff --git a/static/s5968156.jpg b/static/s5968156.jpg new file mode 100644 index 0000000..abb1640 Binary files /dev/null and b/static/s5968156.jpg differ diff --git a/static/s6382631.jpg b/static/s6382631.jpg new file mode 100755 index 0000000..fbaa223 Binary files /dev/null and b/static/s6382631.jpg differ diff --git a/static/search.js b/static/search.js new file mode 100644 index 0000000..7d5681b --- /dev/null +++ b/static/search.js @@ -0,0 +1,363 @@ +(function () { + var INDEXS = {}; + + var LOCAL_STORAGE = { + EXPIRE_KEY: 'docsify.search.expires', + INDEX_KEY: 'docsify.search.index' + }; + + function resolveExpireKey(namespace) { + return namespace ? ((LOCAL_STORAGE.EXPIRE_KEY) + "/" + namespace) : LOCAL_STORAGE.EXPIRE_KEY + } + function resolveIndexKey(namespace) { + return namespace ? ((LOCAL_STORAGE.INDEX_KEY) + "/" + namespace) : LOCAL_STORAGE.INDEX_KEY + } + + function escapeHtml(string) { + var entityMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''', + '/': '/' + }; + + return String(string).replace(/[&<>"'/]/g, function (s) { return entityMap[s]; }) + } + + function getAllPaths(router) { + var paths = []; + + Docsify.dom.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])').forEach(function (node) { + var href = node.href; + var originHref = node.getAttribute('href'); + var path = router.parse(href).path; + + if ( + path && + paths.indexOf(path) === -1 && + !Docsify.util.isAbsolutePath(originHref) + ) { + paths.push(path); + } + }); + + return paths + } + + function saveData(maxAge, expireKey, indexKey) { + localStorage.setItem(expireKey, Date.now() + maxAge); + localStorage.setItem(indexKey, JSON.stringify(INDEXS)); + } + + function genIndex(path, content, router, depth) { + if ( content === void 0 ) content = ''; + + var tokens = window.marked.lexer(content); + var slugify = window.Docsify.slugify; + var index = {}; + var slug; + + tokens.forEach(function (token) { + if (token.type === 'heading' && token.depth <= depth) { + slug = router.toURL(path, {id: slugify(token.text)}); + index[slug] = {slug: slug, title: token.text, body: ''}; + } else { + if (!slug) { + return + } + if (!index[slug]) { + index[slug] = {slug: slug, title: '', body: ''}; + } else if (index[slug].body) { + index[slug].body += '\n' + (token.text || ''); + } else { + index[slug].body = token.text; + } + } + }); + slugify.clear(); + return index + } + + /** + * @param {String} query + * @returns {Array} + */ + function search(query) { + var matchingResults = []; + var data = []; + Object.keys(INDEXS).forEach(function (key) { + data = data.concat(Object.keys(INDEXS[key]).map(function (page) { return INDEXS[key][page]; })); + }); + + query = query.trim(); + var keywords = query.split(/[\s\-,\\/]+/); + if (keywords.length !== 1) { + keywords = [].concat(query, keywords); + } + + var loop = function ( i ) { + var post = data[i]; + var matchesScore = 0; + var resultStr = ''; + var postTitle = post.title && post.title.trim(); + var postContent = post.body && post.body.trim(); + var postUrl = post.slug || ''; + + if (postTitle) { + keywords.forEach( function (keyword) { + // From https://github.com/sindresorhus/escape-string-regexp + var regEx = new RegExp( + keyword.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'), + 'gi' + ); + var indexTitle = -1; + var indexContent = -1; + + indexTitle = postTitle ? postTitle.search(regEx) : -1; + indexContent = postContent ? postContent.search(regEx) : -1; + + if (indexTitle >= 0 || indexContent >= 0) { + matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0; + if (indexContent < 0) { + indexContent = 0; + } + + var start = 0; + var end = 0; + + start = indexContent < 11 ? 0 : indexContent - 10; + end = start === 0 ? 70 : indexContent + keyword.length + 60; + + if (end > postContent.length) { + end = postContent.length; + } + + var matchContent = + '...' + + escapeHtml(postContent) + .substring(start, end) + .replace(regEx, ("" + keyword + "")) + + '...'; + + resultStr += matchContent; + } + }); + + if (matchesScore > 0) { + var matchingPost = { + title: escapeHtml(postTitle), + content: postContent ? resultStr : '', + url: postUrl, + score: matchesScore + }; + + matchingResults.push(matchingPost); + } + } + }; + + for (var i = 0; i < data.length; i++) loop( i ); + + return matchingResults.sort(function (r1, r2) { return r2.score - r1.score; }); + } + + function init(config, vm) { + var isAuto = config.paths === 'auto'; + + var expireKey = resolveExpireKey(config.namespace); + var indexKey = resolveIndexKey(config.namespace); + + var isExpired = localStorage.getItem(expireKey) < Date.now(); + + INDEXS = JSON.parse(localStorage.getItem(indexKey)); + + if (isExpired) { + INDEXS = {}; + } else if (!isAuto) { + return + } + + var paths = isAuto ? getAllPaths(vm.router) : config.paths; + var len = paths.length; + var count = 0; + + paths.forEach(function (path) { + if (INDEXS[path]) { + return count++ + } + + Docsify + .get(vm.router.getFile(path), false, vm.config.requestHeaders) + .then(function (result) { + INDEXS[path] = genIndex(path, result, vm.router, config.depth); + len === ++count && saveData(config.maxAge, expireKey, indexKey); + }); + }); + } + + var NO_DATA_TEXT = ''; + var options; + + function style() { + var code = "\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"; + + Docsify.dom.style(code); + } + + function tpl(defaultValue) { + if ( defaultValue === void 0 ) defaultValue = ''; + + var html = + "
    \n \n
    \n \n \n \n \n \n
    \n
    \n
    \n "; + var el = Docsify.dom.create('div', html); + var aside = Docsify.dom.find('aside'); + + Docsify.dom.toggleClass(el, 'search'); + Docsify.dom.before(aside, el); + } + + function doSearch(value) { + var $search = Docsify.dom.find('div.search'); + var $panel = Docsify.dom.find($search, '.results-panel'); + var $clearBtn = Docsify.dom.find($search, '.clear-button'); + var $sidebarNav = Docsify.dom.find('.sidebar-nav'); + var $appName = Docsify.dom.find('.app-name'); + + if (!value) { + $panel.classList.remove('show'); + $clearBtn.classList.remove('show'); + $panel.innerHTML = ''; + + if (options.hideOtherSidebarContent) { + $sidebarNav.classList.remove('hide'); + $appName.classList.remove('hide'); + } + return + } + var matchs = search(value); + + var html = ''; + matchs.forEach(function (post) { + html += "
    "; + }); + + $panel.classList.add('show'); + $clearBtn.classList.add('show'); + $panel.innerHTML = html || ("

    " + NO_DATA_TEXT + "

    "); + if (options.hideOtherSidebarContent) { + $sidebarNav.classList.add('hide'); + $appName.classList.add('hide'); + } + } + + function bindEvents() { + var $search = Docsify.dom.find('div.search'); + var $input = Docsify.dom.find($search, 'input'); + var $inputWrap = Docsify.dom.find($search, '.input-wrap'); + + var timeId; + // Prevent to Fold sidebar + Docsify.dom.on( + $search, + 'click', + function (e) { return e.target.tagName !== 'A' && e.stopPropagation(); } + ); + Docsify.dom.on($input, 'input', function (e) { + clearTimeout(timeId); + timeId = setTimeout(function (_) { return doSearch(e.target.value.trim()); }, 100); + }); + Docsify.dom.on($inputWrap, 'click', function (e) { + // Click input outside + if (e.target.tagName !== 'INPUT') { + $input.value = ''; + doSearch(); + } + }); + } + + function updatePlaceholder(text, path) { + var $input = Docsify.dom.getNode('.search input[type="search"]'); + + if (!$input) { + return + } + if (typeof text === 'string') { + $input.placeholder = text; + } else { + var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0]; + $input.placeholder = text[match]; + } + } + + function updateNoData(text, path) { + if (typeof text === 'string') { + NO_DATA_TEXT = text; + } else { + var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0]; + NO_DATA_TEXT = text[match]; + } + } + + function updateOptions(opts) { + options = opts; + } + + function init$1(opts, vm) { + var keywords = vm.router.parse().query.s; + + updateOptions(opts); + style(); + tpl(keywords); + bindEvents(); + keywords && setTimeout(function (_) { return doSearch(keywords); }, 500); + } + + function update(opts, vm) { + updateOptions(opts); + updatePlaceholder(opts.placeholder, vm.route.path); + updateNoData(opts.noData, vm.route.path); + } + + var CONFIG = { + placeholder: 'Type to search', + noData: 'No Results!', + paths: 'auto', + depth: 2, + maxAge: 86400000, // 1 day + hideOtherSidebarContent: false, + namespace: undefined + }; + + var install = function (hook, vm) { + var util = Docsify.util; + var opts = vm.config.search || CONFIG; + + if (Array.isArray(opts)) { + CONFIG.paths = opts; + } else if (typeof opts === 'object') { + CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto'; + CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge; + CONFIG.placeholder = opts.placeholder || CONFIG.placeholder; + CONFIG.noData = opts.noData || CONFIG.noData; + CONFIG.depth = opts.depth || CONFIG.depth; + CONFIG.hideOtherSidebarContent = opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent; + CONFIG.namespace = opts.namespace || CONFIG.namespace; + } + + var isAuto = CONFIG.paths === 'auto'; + + hook.mounted(function (_) { + init$1(CONFIG, vm); + !isAuto && init(CONFIG, vm); + }); + hook.doneEach(function (_) { + update(CONFIG, vm); + isAuto && init(CONFIG, vm); + }); + }; + + $docsify.plugins = [].concat(install, $docsify.plugins); + +}()); diff --git a/static/search1.js b/static/search1.js new file mode 100644 index 0000000..b0a5eca --- /dev/null +++ b/static/search1.js @@ -0,0 +1,363 @@ +(function () { + var INDEXS = {}; + + var LOCAL_STORAGE = { + EXPIRE_KEY: 'docsify.search.expires', + INDEX_KEY: 'docsify.search.index' + }; + + function resolveExpireKey(namespace) { + return namespace ? ((LOCAL_STORAGE.EXPIRE_KEY) + "/" + namespace) : LOCAL_STORAGE.EXPIRE_KEY + } + function resolveIndexKey(namespace) { + return namespace ? ((LOCAL_STORAGE.INDEX_KEY) + "/" + namespace) : LOCAL_STORAGE.INDEX_KEY + } + + function escapeHtml(string) { + var entityMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''', + '/': '/' + }; + + return String(string).replace(/[&<>"'/]/g, function (s) { return entityMap[s]; }) + } + + function getAllPaths(router) { + var paths = []; + + Docsify.dom.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])').forEach(function (node) { + var href = node.href; + var originHref = node.getAttribute('href'); + var path = router.parse(href).path; + + if ( + path && + paths.indexOf(path) === -1 && + !Docsify.util.isAbsolutePath(originHref) + ) { + paths.push(path); + } + }); + + return paths + } + + function saveData(maxAge, expireKey, indexKey) { + localStorage.setItem(expireKey, Date.now() + maxAge); + localStorage.setItem(indexKey, JSON.stringify(INDEXS)); + } + + function genIndex(path, content, router, depth) { + if ( content === void 0 ) content = ''; + + var tokens = window.marked.lexer(content); + var slugify = window.Docsify.slugify; + var index = {}; + var slug; + + tokens.forEach(function (token) { + if (token.type === 'heading' && token.depth <= depth) { + slug = router.toURL(path, {id: slugify(token.text)}); + index[slug] = {slug: slug, title: token.text, body: ''}; + } else { + if (!slug) { + return + } + if (!index[slug]) { + index[slug] = {slug: slug, title: '', body: ''}; + } else if (index[slug].body) { + index[slug].body += '\n' + (token.text || ''); + } else { + index[slug].body = token.text; + } + } + }); + slugify.clear(); + return index + } + + /** + * @param {String} query + * @returns {Array} + */ + function search(query) { + var matchingResults = []; + var data = []; + Object.keys(INDEXS).forEach(function (key) { + data = data.concat(Object.keys(INDEXS[key]).map(function (page) { return INDEXS[key][page]; })); + }); + + query = query.trim(); + var keywords = query.split(/[\s\-,\\/]+/); + if (keywords.length !== 1) { + keywords = [].concat(query, keywords); + } + + var loop = function ( i ) { + var post = data[i]; + var matchesScore = 0; + var resultStr = ''; + var postTitle = post.title && post.title.trim(); + var postContent = post.body && post.body.trim(); + var postUrl = post.slug || ''; + + if (postTitle) { + keywords.forEach( function (keyword) { + // From https://github.com/sindresorhus/escape-string-regexp + var regEx = new RegExp( + keyword.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'), + 'gi' + ); + var indexTitle = -1; + var indexContent = -1; + + indexTitle = postTitle ? postTitle.search(regEx) : -1; + indexContent = postContent ? postContent.search(regEx) : -1; + + if (indexTitle >= 0 || indexContent >= 0) { + matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0; + if (indexContent < 0) { + indexContent = 0; + } + + var start = 0; + var end = 0; + + start = indexContent < 11 ? 0 : indexContent - 10; + end = start === 0 ? 70 : indexContent + keyword.length + 60; + + if (end > postContent.length) { + end = postContent.length; + } + + var matchContent = + '...' + + escapeHtml(postContent) + .substring(start, end) + .replace(regEx, ("" + keyword + "")) + + '...'; + + resultStr += matchContent; + } + }); + + if (matchesScore > 0) { + var matchingPost = { + title: escapeHtml(postTitle), + content: postContent ? resultStr : '', + url: postUrl, + score: matchesScore + }; + + matchingResults.push(matchingPost); + } + } + }; + + for (var i = 0; i < data.length; i++) loop( i ); + + return matchingResults.sort(function (r1, r2) { return r2.score - r1.score; }); + } + + function init(config, vm) { + var isAuto = config.paths === 'auto'; + + var expireKey = resolveExpireKey(config.namespace); + var indexKey = resolveIndexKey(config.namespace); + + var isExpired = localStorage.getItem(expireKey) < Date.now(); + + INDEXS = JSON.parse(localStorage.getItem(indexKey)); + + if (isExpired) { + INDEXS = {}; + } else if (!isAuto) { + return + } + + var paths = isAuto ? getAllPaths(vm.router) : config.paths; + var len = paths.length; + var count = 0; + + paths.forEach(function (path) { + if (INDEXS[path]) { + return count++ + } + + Docsify + .get(vm.router.getFile(path), false, vm.config.requestHeaders) + .then(function (result) { + INDEXS[path] = genIndex(path, result, vm.router, config.depth); + len === ++count && saveData(config.maxAge, expireKey, indexKey); + }); + }); + } + + var NO_DATA_TEXT = ''; + var options; + + function style() { + var code = "\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"; + + Docsify.dom.style(code); + } + + function tpl(defaultValue) { + if ( defaultValue === void 0 ) defaultValue = ''; + + var html = + "
    \n \n
    \n \n \n \n \n \n
    \n
    \n
    \n "; + var el = Docsify.dom.create('div', html); + var aside = Docsify.dom.find('aside'); + + Docsify.dom.toggleClass(el, 'search'); + Docsify.dom.before(aside, el); + } + + function doSearch(value) { + var $search = Docsify.dom.find('div.search'); + var $panel = Docsify.dom.find($search, '.results-panel'); + var $clearBtn = Docsify.dom.find($search, '.clear-button'); + var $sidebarNav = Docsify.dom.find('.sidebar-nav'); + var $appName = Docsify.dom.find('.app-name'); + + if (!value) { + $panel.classList.remove('show'); + $clearBtn.classList.remove('show'); + $panel.innerHTML = ''; + + if (options.hideOtherSidebarContent) { + $sidebarNav.classList.remove('hide'); + $appName.classList.remove('hide'); + } + return + } + var matchs = search(value); + + var html = ''; + matchs.forEach(function (post) { + html += ""; + }); + + $panel.classList.add('show'); + $clearBtn.classList.add('show'); + $panel.innerHTML = html || ("

    " + NO_DATA_TEXT + "

    "); + if (options.hideOtherSidebarContent) { + $sidebarNav.classList.add('hide'); + $appName.classList.add('hide'); + } + } + + function bindEvents() { + var $search = Docsify.dom.find('div.search'); + var $input = Docsify.dom.find($search, 'input'); + var $inputWrap = Docsify.dom.find($search, '.input-wrap'); + + var timeId; + // Prevent to Fold sidebar + Docsify.dom.on( + $search, + 'click', + function (e) { return e.target.tagName !== 'A' && e.stopPropagation(); } + ); + Docsify.dom.on($input, 'input', function (e) { + clearTimeout(timeId); + timeId = setTimeout(function (_) { return doSearch(e.target.value.trim()); }, 100); + }); + Docsify.dom.on($inputWrap, 'click', function (e) { + // Click input outside + if (e.target.tagName !== 'INPUT') { + $input.value = ''; + doSearch(); + } + }); + } + + function updatePlaceholder(text, path) { + var $input = Docsify.dom.getNode('.search input[type="search"]'); + + if (!$input) { + return + } + if (typeof text === 'string') { + $input.placeholder = text; + } else { + var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0]; + $input.placeholder = text[match]; + } + } + + function updateNoData(text, path) { + if (typeof text === 'string') { + NO_DATA_TEXT = text; + } else { + var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0]; + NO_DATA_TEXT = text[match]; + } + } + + function updateOptions(opts) { + options = opts; + } + + function init$1(opts, vm) { + var keywords = vm.router.parse().query.s; + + updateOptions(opts); + style(); + tpl(keywords); + bindEvents(); + keywords && setTimeout(function (_) { return doSearch(keywords); }, 500); + } + + function update(opts, vm) { + updateOptions(opts); + updatePlaceholder(opts.placeholder, vm.route.path); + updateNoData(opts.noData, vm.route.path); + } + + var CONFIG = { + placeholder: 'Type to search', + noData: 'No Results!', + paths: 'auto', + depth: 2, + maxAge: 86400000, // 1 day + hideOtherSidebarContent: false, + namespace: undefined + }; + + var install = function (hook, vm) { + var util = Docsify.util; + var opts = vm.config.search || CONFIG; + + if (Array.isArray(opts)) { + CONFIG.paths = opts; + } else if (typeof opts === 'object') { + CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto'; + CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge; + CONFIG.placeholder = opts.placeholder || CONFIG.placeholder; + CONFIG.noData = opts.noData || CONFIG.noData; + CONFIG.depth = opts.depth || CONFIG.depth; + CONFIG.hideOtherSidebarContent = opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent; + CONFIG.namespace = opts.namespace || CONFIG.namespace; + } + + var isAuto = CONFIG.paths === 'auto'; + + hook.mounted(function (_) { + init$1(CONFIG, vm); + !isAuto && init(CONFIG, vm); + }); + hook.doneEach(function (_) { + update(CONFIG, vm); + isAuto && init(CONFIG, vm); + }); + }; + + $docsify.plugins = [].concat(install, $docsify.plugins); + +}()); \ No newline at end of file diff --git a/static/searchgrid2.jpg b/static/searchgrid2.jpg new file mode 100644 index 0000000..69db95b Binary files /dev/null and b/static/searchgrid2.jpg differ diff --git a/static/sort_list_1.jpg b/static/sort_list_1.jpg new file mode 100644 index 0000000..ddc64b5 Binary files /dev/null and b/static/sort_list_1.jpg differ diff --git a/static/sychronize.png b/static/sychronize.png new file mode 100755 index 0000000..2e65e74 Binary files /dev/null and b/static/sychronize.png differ diff --git a/static/tctip-1.0.0.min.js b/static/tctip-1.0.0.min.js new file mode 100644 index 0000000..4eaa98f --- /dev/null +++ b/static/tctip-1.0.0.min.js @@ -0,0 +1,5 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("tctip",[],e):"object"==typeof exports?exports.tctip=e():t.tctip=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=46)}([function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function A(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var n=0;n=0)return a}}catch(t){r=!0,i=t}finally{try{!n&&A.return&&A.return()}finally{if(r)throw i}}return"other"}Object.defineProperty(e,"__esModule",{value:!0});var a="innerText";"firefox"===A()&&(e.textKey=a="textContent"),e.ready=r,e.createElement=i,e.getElementsByClassName=o,e.textKey=a},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var i=function(){function t(t,e){for(var n=0;n",license:"MIT",bugs:{url:"https://github.com/greedying/tctip/issues"},homepage:"https://github.com/greedying/tctip",dependencies:{"qrcode-generator":"^1.1.0"}}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(20),o=r(i),A=new o.default.EventEmitter;e.default=A,t.exports=e.default},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function A(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var n=0;n=5)break}}catch(t){i=!0,o=t}finally{try{!r&&c.return&&c.return()}finally{if(i)throw o}}n||(e[0].active=!0),t.list=e}Object.defineProperty(e,"__esModule",{value:!0}),e.formatConfig=void 0;var A=n(16),a="10%",c=!0,u=["dashang","zanzhu"],s={id:1,type:"dashang"},l={zanzhu:{1:n(33),2:n(34),3:n(35),4:n(36),5:n(37),6:n(38),7:n(39),8:n(40),9:n(41)},dashang:{1:n(24),2:n(25),3:n(26),4:n(27),5:n(28),6:n(29),7:n(30),8:n(31),9:n(32)}},f={alipay:{icon:n(42),name:"支付宝",desc:"支付宝进群"},wechat:{icon:n(45),name:"微信",desc:""},bitcoin:{icon:n(43),name:"比特币",desc:"比特币进群"},tenpay:{icon:n(44),name:"财付通",desc:"财付通进群"}};e.formatConfig=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(){var t=o+"/version/"+i;document.body.appendChild((0,r.createElement)({src:t},"script"))};var r=n(1),i=n(3).version,o="http://stat.tctip.com/stat/index";t.exports=e.default},function(t,e,n){var r=n(18);"string"==typeof r&&(r=[[t.i,r,""]]);n(22)(r,{});r.locals&&(t.exports=r.locals)},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function A(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var n=0;n0&&this._events[t].length>i&&(this._events[t].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[t].length),"function"==typeof console.trace&&console.trace())),this},n.prototype.on=n.prototype.addListener,n.prototype.once=function(t,e){function n(){this.removeListener(t,n),i||(i=!0,e.apply(this,arguments))}if(!r(e))throw TypeError("listener must be a function");var i=!1;return n.listener=e,this.on(t,n),this},n.prototype.removeListener=function(t,e){var n,i,A,a;if(!r(e))throw TypeError("listener must be a function");if(!this._events||!this._events[t])return this;if(n=this._events[t],A=n.length,i=-1,n===e||r(n.listener)&&n.listener===e)delete this._events[t],this._events.removeListener&&this.emit("removeListener",t,e);else if(o(n)){for(a=A;a-- >0;)if(n[a]===e||n[a].listener&&n[a].listener===e){i=a;break}if(i<0)return this;1===n.length?(n.length=0,delete this._events[t]):n.splice(i,1),this._events.removeListener&&this.emit("removeListener",t,e)}return this},n.prototype.removeAllListeners=function(t){var e,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[t]&&delete this._events[t],this;if(0===arguments.length){for(e in this._events)"removeListener"!==e&&this.removeAllListeners(e);return this.removeAllListeners("removeListener"),this._events={},this}if(n=this._events[t],r(n))this.removeListener(t,n);else if(n)for(;n.length;)this.removeListener(t,n[n.length-1]);return delete this._events[t],this},n.prototype.listeners=function(t){var e;return e=this._events&&this._events[t]?r(this._events[t])?[this._events[t]]:this._events[t].slice():[]},n.prototype.listenerCount=function(t){if(this._events){var e=this._events[t];if(r(e))return 1;if(e)return e.length}return 0},n.listenerCount=function(t,e){return t.listenerCount(e)}},function(t,e,n){var r,i,o,A=function(){function t(e,n){if("undefined"==typeof e.length)throw new Error(e.length+"/"+n);var r=function(){for(var t=0;t=7&&D(t),null==y&&(y=U(p,h,m)),k(y,e)},w=function(t,e){for(var n=-1;n<=7;n+=1)if(!(t+n<=-1||d<=t+n))for(var r=-1;r<=7;r+=1)e+r<=-1||d<=e+r||(0<=n&&n<=6&&(0==r||6==r)||0<=r&&r<=6&&(0==n||6==n)||2<=n&&n<=4&&2<=r&&r<=4?g[t+n][e+r]=!0:g[t+n][e+r]=!1)},I=function(){for(var t=0,e=0,n=0;n<8;n+=1){E(!0,n);var r=o.getLostPoint(v);(0==n||t>r)&&(t=r,e=n)}return e},R=function(){for(var t=8;t>n&1);g[Math.floor(n/3)][n%3+d-8-3]=r}for(var n=0;n<18;n+=1){var r=!t&&1==(e>>n&1);g[n%3+d-8-3][Math.floor(n/3)]=r}},B=function(t,e){for(var n=h<<3|e,r=o.getBCHTypeInfo(n),i=0;i<15;i+=1){var A=!t&&1==(r>>i&1);i<6?g[i][8]=A:i<8?g[i+1][8]=A:g[d-15+i][8]=A}for(var i=0;i<15;i+=1){var A=!t&&1==(r>>i&1);i<8?g[8][d-i-1]=A:i<9?g[8][15-i-1+1]=A:g[8][15-i-1]=A}g[d-8][8]=!t},k=function(t,e){for(var n=-1,r=d-1,i=7,A=0,a=o.getMaskFunction(e),c=d-1;c>0;c-=2)for(6==c&&(c-=1);;){for(var u=0;u<2;u+=1)if(null==g[r][c-u]){var s=!1;A>>i&1));var l=a(r,c-u);l&&(s=!s),g[r][c-u]=s,i-=1,i==-1&&(A+=1,i=7)}if(r+=n,r<0||d<=r){r-=n,n=-n;break}}},j=function(e,n){for(var r=0,i=0,A=0,a=new Array(n.length),c=new Array(n.length),u=0;u=0?g.getAt(d):0}}for(var b=0,f=0;f8*f)throw new Error("code length overflow. ("+u.getLengthInBits()+">"+8*f+")");for(u.getLengthInBits()+4<=8*f&&u.put(0,4);u.getLengthInBits()%8!=0;)u.putBit(!1);for(;;){if(u.getLengthInBits()>=8*f)break;if(u.put(i,8),u.getLengthInBits()>=8*f)break;u.put(A,8)}return j(u,r)};return v.addData=function(t,e){e=e||"Byte";var n=null;switch(e){case"Numeric":n=u(t);break;case"Alphanumeric":n=s(t);break;case"Byte":n=l(t);break;case"Kanji":n=f(t);break;default:throw"mode:"+e}m.push(n),y=null},v.isDark=function(t,e){if(t<0||d<=t||e<0||d<=e)throw new Error(t+","+e);return g[t][e]},v.getModuleCount=function(){return d},v.make=function(){E(!1,I())},v.createTableTag=function(t,e){t=t||2,e="undefined"==typeof e?4*t:e;var n="";n+='";for(var i=0;i';n+=""}return n+="",n+="
    "},v.createSvgTag=function(t,e){t=t||2,e="undefined"==typeof e?4*t:e;var n,r,i,o,A,a=v.getModuleCount()*t+2*e,c="";for(A="l"+t+",0 0,"+t+" -"+t+",0 0,-"+t+"z ",c+=">>8),e.push(255&A)):e.push(r)}}return e}};var n={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},r={L:1,M:0,Q:3,H:2},i={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},o=function(){var e=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],r=1335,o=7973,a=21522,c={},u=function(t){for(var e=0;0!=t;)e+=1,t>>>=1;return e};return c.getBCHTypeInfo=function(t){for(var e=t<<10;u(e)-u(r)>=0;)e^=r<=0;)e^=o<5&&(n+=3+o-5)}for(var r=0;r=256;)e-=255;return t[e]},r}(),a=function(){var t=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12,7,37,13],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],e=function(t,e){var n={};return n.totalCount=t,n.dataCount=e,n},n={},i=function(e,n){switch(console.log(e),console.log(n),n){case r.L:return t[4*(e-1)+0];case r.M:return t[4*(e-1)+1];case r.Q:return t[4*(e-1)+2];case r.H:return t[4*(e-1)+3];default:return}};return n.getRSBlocks=function(t,n){var r=i(t,n);if("undefined"==typeof r)throw new Error("bad rs block @ typeNumber:"+t+"/errorCorrectionLevel:"+n);for(var o=r.length/3,A=new Array,a=0;a>>7-e%8&1)},n.put=function(t,e){for(var r=0;r>>e-r-1&1))},n.getLengthInBits=function(){return e},n.putBit=function(n){var r=Math.floor(e/8);t.length<=r&&t.push(0),n&&(t[r]|=128>>>e%8),e+=1},n},u=function(t){var e=n.MODE_NUMBER,r=t,i={};i.getMode=function(){return e},i.getLength=function(t){return r.length},i.write=function(t){for(var e=r,n=0;n+2>>8&255)+(255&r),t.put(r,13),n+=2}if(n>>8)},e.writeBytes=function(t,n,r){n=n||0,r=r||t.length;for(var i=0;i0&&(e+=","),e+=t[n];return e+="]"},e},h=function(){var t=0,e=0,n=0,r="",i={},o=function(t){r+=String.fromCharCode(A(63&t))},A=function(t){if(t<0);else{if(t<26)return 65+t;if(t<52)return 97+(t-26);if(t<62)return 48+(t-52);if(62==t)return 43;if(63==t)return 47}throw new Error("n:"+t)};return i.writeByte=function(r){for(t=t<<8|255&r,e+=8,n+=1;e>=6;)o(t>>>e-6),e-=6},i.flush=function(){if(e>0&&(o(t<<6-e),t=0,e=0),n%3!=0)for(var i=3-n%3,A=0;A=e.length){if(0==i)return-1;throw new Error("unexpected end of file./"+i)}var t=e.charAt(n);if(n+=1,"="==t)return i=0,-1;t.match(/^\s$/)||(r=r<<6|A(t.charCodeAt(0)),i+=6)}var o=r>>>i-8&255;return i-=8,o};var A=function(t){if(65<=t&&t<=90)return t-65;if(97<=t&&t<=122)return t-97+26;if(48<=t&&t<=57)return t-48+52;if(43==t)return 62;if(47==t)return 63;throw new Error("c:"+t)};return o},d=function(t,e){var n=t,r=e,i=new Array(t*e),o={};o.setPixel=function(t,e,r){i[e*n+t]=r},o.write=function(t){t.writeString("GIF87a"),t.writeShort(n),t.writeShort(r),t.writeByte(128),t.writeByte(0),t.writeByte(0),t.writeByte(0),t.writeByte(0),t.writeByte(0),t.writeByte(255),t.writeByte(255),t.writeByte(255),t.writeString(","),t.writeShort(0),t.writeShort(0),t.writeShort(n),t.writeShort(r),t.writeByte(0);var e=2,i=a(e);t.writeByte(e);for(var o=0;i.length-o>255;)t.writeByte(255),t.writeBytes(i,o,255),o+=255;t.writeByte(i.length-o),t.writeBytes(i,o,i.length-o),t.writeByte(0),t.writeString(";")};var A=function(t){var e=t,n=0,r=0,i={};return i.write=function(t,i){if(t>>>i!=0)throw new Error("length over");for(;n+i>=8;)e.writeByte(255&(t<>>=8-n,r=0,n=0;r|=t<0&&e.writeByte(r)},i},a=function(t){for(var e=1<=0&&y.splice(e,1)}function A(t){var e=document.createElement("style");return e.type="text/css",i(t,e),e}function a(t){var e=document.createElement("link");return e.rel="stylesheet",i(t,e),e}function c(t,e){var n,r,i;if(e.singleton){var c=b++;n=d||(d=A(e)),r=u.bind(null,n,c,!1),i=u.bind(null,n,c,!0)}else t.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=a(e),r=l.bind(null,n),i=function(){o(n),n.href&&URL.revokeObjectURL(n.href)}):(n=A(e),r=s.bind(null,n),i=function(){o(n)});return r(t),function(e){if(e){if(e.css===t.css&&e.media===t.media&&e.sourceMap===t.sourceMap)return;r(t=e)}else i()}}function u(t,e,n,r){var i=n?"":r.css;if(t.styleSheet)t.styleSheet.cssText=m(e,i);else{var o=document.createTextNode(i),A=t.childNodes;A[e]&&t.removeChild(A[e]),A.length?t.insertBefore(o,A[e]):t.appendChild(o)}}function s(t,e){var n=e.css,r=e.media;if(r&&t.setAttribute("media",r),t.styleSheet)t.styleSheet.cssText=n;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(n))}}function l(t,e){var n=e.css,r=e.sourceMap;r&&(n+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */");var i=new Blob([n],{type:"text/css"}),o=t.href;t.href=URL.createObjectURL(i),o&&URL.revokeObjectURL(o)}var f={},p=function(t){var e;return function(){return"undefined"==typeof e&&(e=t.apply(this,arguments)),e}},h=p(function(){return/msie [6-9]\b/.test(self.navigator.userAgent.toLowerCase())}),g=p(function(){return document.head||document.getElementsByTagName("head")[0]}),d=null,b=0,y=[];t.exports=function(t,e){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");e=e||{},"undefined"==typeof e.singleton&&(e.singleton=h()),"undefined"==typeof e.insertAt&&(e.insertAt="bottom");var i=r(t);return n(i,e),function(t){for(var o=[],A=0;Anav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}.emoji{height:1.2rem;vertical-align:middle}.progress{background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search .search-keyword,.search a:hover{color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30}.sidebar-toggle .sidebar-toggle-button:hover{opacity:.4}.sidebar-toggle span{background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:800px;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{-webkit-animation:none;animation:none}.github-corner .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}}@-webkit-keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;height:100%;width:100%}section.cover .cover-main{flex:1;margin:-20px 16px 0;text-align:center;z-index:1}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid var(--theme-color,#42b983);box-sizing:border-box;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code{border-radius:2px;color:#e96900;font-size:.8rem;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section code,.markdown-section pre{background-color:#f8f8f8;font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section pre{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;line-height:1.5rem;margin:1.2em 0;overflow:auto;padding:0 1.4rem;position:relative;word-wrap:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;background-color:#f8f8f8;border-radius:2px;color:#525252;display:block;font-family:Roboto Mono,Monaco,courier,monospace;font-size:.8rem;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;padding:2.2em 5px;white-space:inherit}.markdown-section code:after,.markdown-section code:before{letter-spacing:.05rem}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto}pre:after{color:#ccc;content:attr(data-lang);font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0} \ No newline at end of file diff --git a/static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5NDY4NTcz,size_16,color_FFFFFF,t_70.jpeg b/static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5NDY4NTcz,size_16,color_FFFFFF,t_70.jpeg new file mode 100644 index 0000000..feddcbc Binary files /dev/null and b/static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5NDY4NTcz,size_16,color_FFFFFF,t_70.jpeg differ diff --git a/static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDEyNTc5,size_16,color_FFFFFF,t_70.jpeg b/static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDEyNTc5,size_16,color_FFFFFF,t_70.jpeg new file mode 100644 index 0000000..0dda428 Binary files /dev/null and b/static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDEyNTc5,size_16,color_FFFFFF,t_70.jpeg differ diff --git a/static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p1b3lpZ2VoYWl6ZWk=,size_16,color_FFFFFF,t_70.png b/static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p1b3lpZ2VoYWl6ZWk=,size_16,color_FFFFFF,t_70.png new file mode 100644 index 0000000..6114df9 Binary files /dev/null and b/static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p1b3lpZ2VoYWl6ZWk=,size_16,color_FFFFFF,t_70.png differ diff --git a/static/watermark.jpeg b/static/watermark.jpeg new file mode 100644 index 0000000..e44c970 Binary files /dev/null and b/static/watermark.jpeg differ diff --git a/static/wdsfsdfsmaster.gif b/static/wdsfsdfsmaster.gif new file mode 100644 index 0000000..9cbae53 Binary files /dev/null and b/static/wdsfsdfsmaster.gif differ diff --git a/static/wdsfsdfsmaster.png b/static/wdsfsdfsmaster.png new file mode 100755 index 0000000..30961a6 Binary files /dev/null and b/static/wdsfsdfsmaster.png differ