全栈
2021-01-16 14:19:04 25 举报
AI智能生成
一个前端工程师的思维导图
作者其他创作
大纲/内容
iOS
iOS 源码剖析
网络请求
PPNetworkHelper分析
PPNetworkHelper
PPNetworkHelper
继承NSObject
+ (BOOL)isNetwork
/// 有网YES, 无网:NO
+ (BOOL)isWWANNetwork
/// 手机网络:YES, 反之:NO
+ (BOOL)isWiFiNetwork
/// WiFi网络:YES, 反之:NO
+ (void)cancelAllRequest
/// 取消所有HTTP请求
+ (void)networkStatusWithBlock:(PPNetworkStatus)networkStatus
/// 实时获取网络状态,通过Block回调实时获取(此方法可多次调用)
+ (void)cancelRequestWithURL:(NSString *)URL
/// 取消指定URL的HTTP请求
+ (void)openLog
/// 开启日志打印 (Debug级别)
+ (void)closeLog
/// 关闭日志打印,默认关闭
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
parameters:(id)parameters
success:(PPHttpRequestSuccess)success
failure:(PPHttpRequestFailed)failure
GET请求,无缓存
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
parameters:(id)parameters
responseCache:(PPHttpRequestCache)responseCache
success:(PPHttpRequestSuccess)success
failure:(PPHttpRequestFailed)failure
GET请求,自动缓存
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
parameters:(id)parameters
success:(PPHttpRequestSuccess)success
failure:(PPHttpRequestFailed)failure
POST请求,无缓存
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
parameters:(id)parameters
responseCache:(PPHttpRequestCache)responseCache
success:(PPHttpRequestSuccess)success
failure:(PPHttpRequestFailed)failure
POST请求,自动缓存
+ (__kindof NSURLSessionTask *)uploadFileWithURL:(NSString *)URL
parameters:(id)parameters
name:(NSString *)name
filePath:(NSString *)filePath
progress:(PPHttpProgress)progress
success:(PPHttpRequestSuccess)success
failure:(PPHttpRequestFailed)failure
上传文件
+ (__kindof NSURLSessionTask *)uploadImagesWithURL:(NSString *)URL
parameters:(id)parameters
name:(NSString *)name
images:(NSArray<UIImage *> *)images
fileNames:(NSArray<NSString *> *)fileNames
imageScale:(CGFloat)imageScale
imageType:(NSString *)imageType
progress:(PPHttpProgress)progress
success:(PPHttpRequestSuccess)success
failure:(PPHttpRequestFailed)failure
上传单/多张图片
+ (__kindof NSURLSessionTask *)downloadWithURL:(NSString *)URL
fileDir:(NSString *)fileDir
progress:(PPHttpProgress)progress
success:(void(^)(NSString *filePath))success
failure:(PPHttpRequestFailed)failure
下载文件
设置AFHTTPSessionManager相关属性
+ (void)setAFHTTPSessionManagerProperty:(void(^)(AFHTTPSessionManager *sessionManager))sessionManager
自定义设置AFHTTPSessionManager实例
+ (void)setRequestSerializer:(PPRequestSerializer)requestSerializer
设置网络请求参数的格式:默认为二进制格式
+ (void)setResponseSerializer:(PPResponseSerializer)responseSerializer
设置服务器响应数据格式:默认为JSON格式
+ (void)setRequestTimeoutInterval:(NSTimeInterval)time
设置请求超时时间:默认为30S
+ (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field
/// 设置请求头
+ (void)openNetworkActivityIndicator:(BOOL)open
是否打开网络状态转圈菊花:默认打开
+ (void)setSecurityPolicyWithCerPath:(NSString *)cerPath validatesDomainName:(BOOL)validatesDomainName
自建Https证书的路径
kIsNetwork
// 一次性判断是否有网的宏
kIsWWANNetwork
一次性判断是否为手机网络的宏
kIsWiFiNetwork
一次性判断是否为WiFi网络的宏
PPNetworkStatusType
PPNetworkStatusUnknown
/// 未知网络
PPNetworkStatusNotReachable
/// 无网络
PPNetworkStatusReachableViaWWAN
/// 手机网络
PPNetworkStatusReachableViaWiFi
/// WIFI网络
PPRequestSerializer
请求数据格式
PPRequestSerializerJSON
/// 设置请求数据为JSON格式
PPRequestSerializerHTTP
/// 设置请求数据为二进制格式
PPResponseSerializer
响应数据格式
PPResponseSerializerJSON
/// 设置响应数据为JSON格式
PPResponseSerializerHTTP
/// 设置响应数据为二进制格式
typedef void(^PPHttpRequestSuccess)(id responseObject)
/// 请求成功的Block
typedef void(^PPHttpRequestFailed)(NSError *error)
/// 请求失败的Block
typedef void(^PPHttpRequestCache)(id responseCache)
/// 缓存的Block
typedef void (^PPHttpProgress)(NSProgress *progress)
/// 上传或者下载的进度, Progress.completedUnitCount:当前大小 - Progress.totalUnitCount:总大小
typedef void(^PPNetworkStatus)(PPNetworkStatusType status)
/// 网络状态的Block
PPNetworkCache
继承NSObject
+ (void)setHttpCache:(id)httpData URL:(NSString *)URL parameters:(id)parameters
异步缓存网络数据,根据请求的 URL与parameters
* 做KEY存储数据, 这样就能缓存多级页面的数据
* 做KEY存储数据, 这样就能缓存多级页面的数据
+ (id)httpCacheForURL:(NSString *)URL parameters:(id)parameters
根据请求的 URL与parameters 同步取出缓存数据
+ (NSInteger)getAllHttpCacheSize
获取网络缓存的总大小 bytes(字节)
+ (void)removeAllHttpCache
/// 删除所有网络缓存
PPHTTPRequestLayer
示例
PPInterfacedConst
接口的常量
UIKIT_EXTERN NSString *const kLogin
登录的接口地址
UIKIT_EXTERN NSString *const kExit
退出的接口地址
UIKIT_EXTERN NSString *const kApiPrefix
服务器请求的地址
PPHTTPRequest
使用PPNetworkHelper的一个示例
继承NSObject
+ (NSURLSessionTask *)getLoginWithParameters:(id)parameters success:(PPRequestSuccess)success failure:(PPRequestFailure)failure
登录
+ (NSURLSessionTask *)getExitWithParameters:(id)parameters success:(PPRequestSuccess)success failure:(PPRequestFailure)failure
退出
请求失败的block
typedef void(^PPRequestFailure)(NSError *error)
请求成功的block
typedef void(^PPRequestSuccess)(id response)
YTKNetwork分析
YTKBaseRequest
继承NSObject
Request and Response Information
@property (nonatomic, strong, readonly) NSURLSessionTask *requestTask
///底层的NSURLSessionTask。
///
/// @warning 这个值实际上是nil,不应该在请求开始前被访问。
///底层的NSURLSessionTask。
///
/// @warning 这个值实际上是nil,不应该在请求开始前被访问。
@property (nonatomic, strong, readonly) NSURLRequest *currentRequest
@property (nonatomic, strong, readonly) NSURLRequest *originalRequest
@property (nonatomic, strong, readonly) NSHTTPURLResponse *response
@property (nonatomic, readonly) NSInteger responseStatusCode
响应状态代码。
响应状态代码。
@property (nonatomic, strong, readonly, nullable) NSDictionary *responseHeaders
响应标题字段。
响应标题字段。
@property (nonatomic, strong, readonly, nullable) NSData *responseData
响应的原始数据表示。请注意,如果请求失败,这个值可以为零。
响应的原始数据表示。请注意,如果请求失败,这个值可以为零。
@property (nonatomic, strong, readonly, nullable) NSString *responseString
响应的字符串表示。请注意,如果请求失败,这个值可以是nil。
响应的字符串表示。请注意,如果请求失败,这个值可以是nil。
@property (nonatomic, strong, readonly, nullable) id responseObject
///这个序列化的响应对象。该对象的实际类型由
/// `YTKResponseSerializerType`。请注意,如果请求失败,这个值可以是nil.YTKResponseSerializerType。
///
///讨论 如果 "resumableDownloadPath "和DownloadTask正在使用,这个值将会是
///是成功保存文件的路径(NSURL),如果请求失败则为nil。
///这个序列化的响应对象。该对象的实际类型由
/// `YTKResponseSerializerType`。请注意,如果请求失败,这个值可以是nil.YTKResponseSerializerType。
///
///讨论 如果 "resumableDownloadPath "和DownloadTask正在使用,这个值将会是
///是成功保存文件的路径(NSURL),如果请求失败则为nil。
@property (nonatomic, strong, readonly, nullable) id responseJSONObject
///如果你使用`YTKResponseSerializerTypeJSON`,这是一个方便的(和语义的)getter。
///为响应对象。否则该值为nil。
///如果你使用`YTKResponseSerializerTypeJSON`,这是一个方便的(和语义的)getter。
///为响应对象。否则该值为nil。
@property (nonatomic, strong, readonly, nullable) NSError *error
///这个错误可能是序列化错误,也可能是网络错误。如果没有发生任何错误
///这个值将为零。
///这个错误可能是序列化错误,也可能是网络错误。如果没有发生任何错误
///这个值将为零。
@property (nonatomic, readonly, getter=isCancelled) BOOL cancelled
///返回请求任务的取消状态。
///返回请求任务的取消状态。
@property (nonatomic, readonly, getter=isExecuting) BOOL executing
///执行请求任务的状态。
///执行请求任务的状态。
Request Configuration
@property (nonatomic) NSInteger tag
///标签可以用来识别请求。默认值为0。
///标签可以用来识别请求。默认值为0。
@property (nonatomic, strong, nullable) NSDictionary *userInfo
/// 用户信息可以用来存储关于请求的附加信息。默认值为nil。
/// 用户信息可以用来存储关于请求的附加信息。默认值为nil。
@property (nonatomic, weak, nullable) id<YTKRequestDelegate> delegate
/// 请求的委托对象。如果你选择块式回调,你可以忽略它。
///默认为nil。
/// 请求的委托对象。如果你选择块式回调,你可以忽略它。
///默认为nil。
@property (nonatomic, copy, nullable) YTKRequestCompletionBlock successCompletionBlock
/// 成功回调。请注意,如果这个值不是nil,并且`requestFinished`委托方法为
///也实现了,两者都会被执行,但会先调用delegate方法。这个块
///将在主队列中被调用。
/// 成功回调。请注意,如果这个值不是nil,并且`requestFinished`委托方法为
///也实现了,两者都会被执行,但会先调用delegate方法。这个块
///将在主队列中被调用。
@property (nonatomic, copy, nullable) YTKRequestCompletionBlock failureCompletionBlock
/// 失败回调。请注意,如果这个值不是nil,并且`requestFailed`委托方法是一个失败的回调。
///也实现了,两者都会被执行,但会先调用delegate方法。这个块
///将在主队列中被调用。
/// 失败回调。请注意,如果这个值不是nil,并且`requestFailed`委托方法是一个失败的回调。
///也实现了,两者都会被执行,但会先调用delegate方法。这个块
///将在主队列中被调用。
@property (nonatomic, strong, nullable) NSMutableArray<id<YTKRequestAccessory>> *requestAccessories
/// 这可以用来添加多个附件对象。注意,如果你使用 "addAccessory "来添加配件,那么你就会发现,你可以使用 "addAccessory "来添加配件。
///这个数组将被自动创建。默认值为nil。
/// 这可以用来添加多个附件对象。注意,如果你使用 "addAccessory "来添加配件,那么你就会发现,你可以使用 "addAccessory "来添加配件。
///这个数组将被自动创建。默认值为nil。
@property (nonatomic, copy, nullable) AFConstructingBlock constructingBodyBlock
/// 当POST请求中需要时,可以用它来构造HTTP body。默认为nil。
/// 当POST请求中需要时,可以用它来构造HTTP body。默认为nil。
@property (nonatomic, strong, nullable) NSString *resumableDownloadPath
///该值用于执行可恢复下载请求。默认值为nil。
///
/// 讨论 当这个值不是nil时,会使用NSURLSessionDownloadTask。
/// 在请求开始之前,路径上存在的文件将被删除。如果请求成功,文件将被删除。
///自动保存到这个路径,否则响应将被保存到`responseData`。
///和`responseString`。要做到这一点,服务器必须支持 "范围",并在响应中加入
///适当的 "Last-Modified "和/或 "Etag"。详见 "NSURLSessionDownloadTask"。
///该值用于执行可恢复下载请求。默认值为nil。
///
/// 讨论 当这个值不是nil时,会使用NSURLSessionDownloadTask。
/// 在请求开始之前,路径上存在的文件将被删除。如果请求成功,文件将被删除。
///自动保存到这个路径,否则响应将被保存到`responseData`。
///和`responseString`。要做到这一点,服务器必须支持 "范围",并在响应中加入
///适当的 "Last-Modified "和/或 "Etag"。详见 "NSURLSessionDownloadTask"。
@property (nonatomic, copy, nullable) AFURLSessionTaskProgressBlock resumableDownloadProgressBlock
/// 你可以使用这个块来跟踪下载进度。另见 "resumableDownloadPath"。
/// 你可以使用这个块来跟踪下载进度。另见 "resumableDownloadPath"。
@property (nonatomic, copy, nullable) AFURLSessionTaskProgressBlock uploadProgressBlock
/// 你可以用这个块来跟踪上传进度。
/// 你可以用这个块来跟踪上传进度。
@property (nonatomic) YTKRequestPriority requestPriority
/// 请求的优先级。默认值是`YTKRequestPriorityDefault`。
/// 请求的优先级。默认值是`YTKRequestPriorityDefault`。
- (void)setCompletionBlockWithSuccess:(nullable YTKRequestCompletionBlock)success
failure:(nullable YTKRequestCompletionBlock)failure
- (void)clearCompletionBlock
- (void)addAccessory:(id<YTKRequestAccessory>)accessory
///添加请求附件的方便方法。另见 "requestAccessories"。
///添加请求附件的方便方法。另见 "requestAccessories"。
Request Action
- (void)start
将自己添加到请求队列中,并开始请求。
将自己添加到请求队列中,并开始请求。
- (void)stop
///从请求队列中删除自己,并取消请求。
///从请求队列中删除自己,并取消请求。
- (void)startWithCompletionBlockWithSuccess:(nullable YTKRequestCompletionBlock)success
failure:(nullable YTKRequestCompletionBlock)failure
///用块回调启动请求的方便方法。
failure:(nullable YTKRequestCompletionBlock)failure
///用块回调启动请求的方便方法。
Subclass Override
- (void)requestCompletePreprocessor
///请求成功后,在切换到主线程之前,在后台线程上调用。注意如果
///缓存加载完毕,这个方法将在主线程上被调用,就像`requestCompleteFilter`一样。
///请求成功后,在切换到主线程之前,在后台线程上调用。注意如果
///缓存加载完毕,这个方法将在主线程上被调用,就像`requestCompleteFilter`一样。
- (void)requestCompleteFilter
///请求成功后在主线程上调用。
///请求成功后在主线程上调用。
- (void)requestFailedPreprocessor
///在请求失败后但切换到主线程之前,在后台线程上调用。参见
/// `requestCompletePreprocessor`。
///在请求失败后但切换到主线程之前,在后台线程上调用。参见
/// `requestCompletePreprocessor`。
- (void)requestFailedFilter
///当请求失败时在主线程上调用。
///当请求失败时在主线程上调用。
- (NSString *)baseUrl
/// 请求的baseURL。这应该只包含URL的主机部分,例如http://www.example.com。
/// 另见`requestUrl`。
/// 请求的baseURL。这应该只包含URL的主机部分,例如http://www.example.com。
/// 另见`requestUrl`。
- (NSString *)requestUrl
///请求的URL路径。这应该只包含URL的路径部分,例如,/v1/user。参见 `baseUrl`。
///
/// 讨论 这将与`baseUrl`一起使用[NSURL URLWithString:relativeToURL]进行连接。
/// 正因为如此,我们建议在使用时遵循上述规则。
/// 否则,结果URL可能无法正确形成。也请参考`URLString:relativeToURL`。
////了解更多信息。
///
/// 此外,如果`requestUrl`本身是一个有效的URL,它将被用作结果URL,并且。
/// `baseUrl`将被忽略。
///请求的URL路径。这应该只包含URL的路径部分,例如,/v1/user。参见 `baseUrl`。
///
/// 讨论 这将与`baseUrl`一起使用[NSURL URLWithString:relativeToURL]进行连接。
/// 正因为如此,我们建议在使用时遵循上述规则。
/// 否则,结果URL可能无法正确形成。也请参考`URLString:relativeToURL`。
////了解更多信息。
///
/// 此外,如果`requestUrl`本身是一个有效的URL,它将被用作结果URL,并且。
/// `baseUrl`将被忽略。
- (NSString *)cdnUrl
///请求时可选择CDN URL。
///请求时可选择CDN URL。
- (NSTimeInterval)requestTimeoutInterval
///请求超时时间间隔。默认为60s。
///
///讨论 当使用 "resumableDownloadPath"(NSURLSessionDownloadTask)时,会话似乎完全忽略了这一点。
/// `NSURLRequest`的`timeoutInterval`属性。设置超时的一个有效方法是使用
/// `NSURLSessionConfiguration`的`timeoutIntervalForResource`。
///请求超时时间间隔。默认为60s。
///
///讨论 当使用 "resumableDownloadPath"(NSURLSessionDownloadTask)时,会话似乎完全忽略了这一点。
/// `NSURLRequest`的`timeoutInterval`属性。设置超时的一个有效方法是使用
/// `NSURLSessionConfiguration`的`timeoutIntervalForResource`。
- (nullable id)requestArgument
/// 附加请求参数。
/// 附加请求参数。
- (id)cacheFileNameFilterForRequestArgument:(id)argument
///在缓存时,重写这个方法来过滤带有某些参数的请求。
///在缓存时,重写这个方法来过滤带有某些参数的请求。
- (YTKRequestMethod)requestMethod
/// HTTP request method.
/// HTTP request method.
- (YTKRequestSerializerType)requestSerializerType
/// Request serializer type.
/// Request serializer type.
- (YTKResponseSerializerType)responseSerializerType
/// Response serializer type. See also `responseObject`.
/// Response serializer type. See also `responseObject`.
- (nullable NSArray<NSString *> *)requestAuthorizationHeaderFieldArray
///用于HTTP授权的用户名和密码。应该形成@[@"用户名",@"密码"]。
///用于HTTP授权的用户名和密码。应该形成@[@"用户名",@"密码"]。
- (nullable NSDictionary<NSString *, NSString *> *)requestHeaderFieldValueDictionary
///附加的HTTP请求头字段。
///附加的HTTP请求头字段。
- (nullable NSURLRequest *)buildCustomUrlRequest
/// 用这个方法建立自定义请求。如果该方法返回非零值,则返回 "requestUrl"、"requestTimeoutInterval"。
/// "requestArgument"、"allowsCellularAccess"、"requestMethod "和 "requestSerializerType "都将被忽略。
/// 用这个方法建立自定义请求。如果该方法返回非零值,则返回 "requestUrl"、"requestTimeoutInterval"。
/// "requestArgument"、"allowsCellularAccess"、"requestMethod "和 "requestSerializerType "都将被忽略。
- (BOOL)useCDN
///发送请求时应使用CDN。
///发送请求时应使用CDN。
- (BOOL)allowsCellularAccess
///是否允许该请求使用蜂窝无线电(如果存在)。默认为 "是"。
///是否允许该请求使用蜂窝无线电(如果存在)。默认为 "是"。
- (nullable id)jsonValidator
/// 验证器将用于测试`responseJSONObject`是否正确形成。
/// 验证器将用于测试`responseJSONObject`是否正确形成。
- (BOOL)statusCodeValidator
/// 这个验证器将用于测试`responseStatusCode`是否有效。
/// 这个验证器将用于测试`responseStatusCode`是否有效。
YTKBatchRequest
批量请求:汶:原理就是一个for循环
批量请求:汶:原理就是一个for循环
继承NSObject
@property (nonatomic, strong, readonly) NSArray<YTKRequest *> *requestArray
请求的数组
请求的数组
@property (nonatomic, weak, nullable) id<YTKBatchRequestDelegate> delegate
自己的代理是谁
自己的代理是谁
@property (nonatomic, copy, nullable) void (^successCompletionBlock)(YTKBatchRequest *)
成功回调
成功回调
@property (nonatomic, copy, nullable) void (^failureCompletionBlock)(YTKBatchRequest *)
失败回调
失败回调
@property (nonatomic) NSInteger tag
区分batchRequest的标记
区分batchRequest的标记
@property (nonatomic, strong, nullable) NSMutableArray<id<YTKRequestAccessory>> *requestAccessories
/// 这可以用来添加多个附件对象。注意,如果你使用 "addAccessory "来添加配件,那么你可以使用 "addAccessory "来添加配件。
///这个数组将被自动创建。默认值为nil。
/// 这可以用来添加多个附件对象。注意,如果你使用 "addAccessory "来添加配件,那么你可以使用 "addAccessory "来添加配件。
///这个数组将被自动创建。默认值为nil。
@property (nonatomic, strong, readonly, nullable) YTKRequest *failedRequest
///第一个失败的请求(并导致批量请求失败)。
///第一个失败的请求(并导致批量请求失败)。
- (instancetype)initWithRequestArray:(NSArray<YTKRequest *> *)requestArray
/// 创建一个包含一堆请求的 "YTKBatchRequest"。
///
/// @param requestArray 请求用来创建批量请求。
///
/// 创建一个包含一堆请求的 "YTKBatchRequest"。
///
/// @param requestArray 请求用来创建批量请求。
///
- (void)setCompletionBlockWithSuccess:(nullable void (^)(YTKBatchRequest *batchRequest))success
failure:(nullable void (^)(YTKBatchRequest *batchRequest))failure
设置完成的回调
failure:(nullable void (^)(YTKBatchRequest *batchRequest))failure
设置完成的回调
- (void)clearCompletionBlock
清除完成回调
清除完成回调
- (void)addAccessory:(id<YTKRequestAccessory>)accessory
///添加请求附件的方便方法。另见 "requestAccessories"。
///添加请求附件的方便方法。另见 "requestAccessories"。
- (void)start
///将所有请求附加到队列中。
///将所有请求附加到队列中。
- (void)stop
///停止所有批量请求的请求。
///停止所有批量请求的请求。
- (void)startWithCompletionBlockWithSuccess:(nullable void (^)(YTKBatchRequest *batchRequest))success
failure:(nullable void (^)(YTKBatchRequest *batchRequest))failure
/// 用块回调启动批处理请求的方便方法。
failure:(nullable void (^)(YTKBatchRequest *batchRequest))failure
/// 用块回调启动批处理请求的方便方法。
- (BOOL)isDataFromCache
///是否所有响应数据都来自本地缓存。
///是否所有响应数据都来自本地缓存。
YTKBatchRequestAgent
汶:其实就是管理BatchRequest的一个单例数组
汶:其实就是管理BatchRequest的一个单例数组
继承NSObject
- (instancetype)init
+ (instancetype)new
+ (YTKBatchRequestAgent *)sharedAgent
///获取共享的批量请求代理。(单例)
- (void)addBatchRequest:(YTKBatchRequest *)request
/// Add a batch request.
- (void)removeBatchRequest:(YTKBatchRequest *)request
/// 删除之前添加的批量请求。
YTKChainRequest
链式请求
继承NSObject
- (NSArray<YTKBaseRequest *> *)requestArray
所有的请求都存储在这个数组中。
@property (nonatomic, weak, nullable) id<YTKChainRequestDelegate> delegate
///链请求的委托对象。默认为nil。
@property (nonatomic, strong, nullable) NSMutableArray<id<YTKRequestAccessory>> *requestAccessories
/// 这可以用来添加多个附件对象。注意,如果你使用 "addAccessory "来添加配件,那么你可以使用 "addAccessory "来添加配件。
///这个数组将被自动创建。默认值为nil。
///这个数组将被自动创建。默认值为nil。
- (void)addAccessory:(id<YTKRequestAccessory>)accessory
///添加请求附件的方便方法。另见 "requestAccessories"。
- (void)start
///启动链式请求,将链式中的第一个请求加入到请求队列中。
- (void)stop
/// 停止链式请求。剩余的链上请求将被取消。
- (void)addRequest:(YTKBaseRequest *)request callback:(nullable YTKChainCallback)callback
/// 将请求添加到请求链中。
///
/// @param request 要链的请求。
/// @param callback 完成回调。
///
/// @param request 要链的请求。
/// @param callback 完成回调。
YTKChainRequestDelegate
协议
- (void)chainRequestFinished:(YTKChainRequest *)chainRequest
///告诉委托人,链请求已经成功完成。
///
/// @param chainRequest 相应的链请求。
///
/// @param chainRequest 相应的链请求。
- (void)chainRequestFailed:(YTKChainRequest *)chainRequest failedBaseRequest:(YTKBaseRequest*)request
///告诉委托人链请求失败。
///
/// @param chainRequest 对应的链请求。
/// @param request 第一个失败的请求,导致整个请求失败。
///
/// @param chainRequest 对应的链请求。
/// @param request 第一个失败的请求,导致整个请求失败。
typedef void (^YTKChainCallback)(YTKChainRequest *chainRequest, YTKBaseRequest *baseRequest)
YTKChainRequestAgent
与YTKBatchRequestAgent类似
YTKNetworkAgent
YTKNetworkConfig
YTKNetworkPrivate
私有方法和一些分类
YTKNetworkUtils
继承NSObject
+ (BOOL)validateJSON:(id)json withValidator:(id)jsonValidator
+ (void)addDoNotBackupAttribute:(NSString *)path
+ (NSString *)md5StringFromString:(NSString *)string
+ (NSString *)appVersionString
+ (NSStringEncoding)stringEncodingWithRequest:(YTKBaseRequest *)request
+ (BOOL)validateResumeData:(NSData *)data
分类
YTKRequest (Getter)
- (NSString *)cacheBasePath
@interface YTKBaseRequest (Setter)
@property (nonatomic, strong, readwrite) NSURLSessionTask *requestTask
@property (nonatomic, strong, readwrite, nullable) NSData *responseData
@property (nonatomic, strong, readwrite, nullable) id responseJSONObject
@property (nonatomic, strong, readwrite, nullable) id responseObject
@property (nonatomic, strong, readwrite, nullable) NSString *responseString
@property (nonatomic, strong, readwrite, nullable) NSError *error
@interface YTKBaseRequest (RequestAccessory)
- (void)toggleAccessoriesWillStartCallBack
- (void)toggleAccessoriesWillStopCallBack
- (void)toggleAccessoriesDidStopCallBack
YTKRequest
架构设计
SUIMVVMKit
NSObject+SMKCoding
SMKCodingProtocol
+ (NSArray *)smk_allowedCodingPropertyNames
这个数组中的属性名才会进行归档
这个数组中的属性名才会进行归档
+ (NSArray *)smk_ignoredCodingPropertyNames
这个数组中的属性名将会被忽略:不进行归档
这个数组中的属性名将会被忽略:不进行归档
遵守SMKCodingProtocol协议
- (BOOL)smk_writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile
archiver
archiver
+ (instancetype)smk_objectWithFile:(NSString *)path
unarchiver
unarchiver
NSObject+SMKProperties
@property (nonatomic, copy, nonnull) ViewModelBlock viewModelBlock
viewModelBlock
- (nullable NSDictionary *)smk_allProperties
获取一个对象的所有属性
@property (nullable, nonatomic, weak) id<SMKViewMangerProtocol> viewMangerDelegate
@property (nonatomic, copy) ViewMangerInfosBlock viewMangerInfosBlock
@property (nullable, nonatomic, weak) id<SMKViewModelProtocol> viewModelDelegate
@property (nonatomic, copy) ViewModelInfosBlock viewModelInfosBlock
@property (nonatomic, strong) SMKMediator *smk_mediator
@property (nonatomic, copy) NSDictionary *smk_viewMangerInfos
smk_viewMangerInfos
@property (nonatomic, copy) NSDictionary *smk_viewModelInfos
smk_viewModelInfos
使用运行时添加属性
typedef _Nonnull id (^ViewModelBlock)( )
typedef void (^ViewMangerInfosBlock)( )
typedef void (^ViewModelInfosBlock)( )
NSObject+SMKRequest
给底层的类添加这些属性,
是为了给后面调用提供方便。
都是通过runtime来给分类设置
属性值。
给底层的类添加这些属性,
是为了给后面调用提供方便。
都是通过runtime来给分类设置
属性值。
@property (nonatomic, copy, nonnull) NSString *smk_scheme
scheme (eg: http, https, ftp)
@property (nonatomic, copy, nonnull) NSString *smk_host
host
host
@property (nonatomic, copy, nonnull) NSString *smk_path
path
path
@property (nonatomic, assign) SMKRequestMethod smk_method
method
method
@property (nonatomic, copy, nonnull) NSString *smk_url
url (如果设置了url,则不需要在设置scheme,host,path 属性)
url (如果设置了url,则不需要在设置scheme,host,path 属性)
@property (nonatomic, retain, nonnull) id smk_params
@property (nonatomic, retain, nonnull) SMKRequestFileConfig *smk_fileConfig
SMKAction
请求类,包装了
AFN
请求类,包装了
AFN
继承NSObject
@property (nonatomic, assign) NSTimeInterval timeoutInterval
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi
+ (instancetype)sharedAction
- (void)cancelAllOperations
取消所有操作
取消所有操作
- (void)configScheme:(NSString *)scheme host:(NSString *)host
配置全局的scheme和host,若request中重新设置新值,则值为request中设置的新值
配置全局的scheme和host,若request中重新设置新值,则值为request中设置的新值
- (NSURLSessionTask *)sendRequest:
(id)request
progress:(progressBlock)progress
success:(successBlock)success
failure:(failureBlock)failure
发送请求(在外部配置request)
(id)request
progress:(progressBlock)progress
success:(successBlock)success
failure:(failureBlock)failure
发送请求(在外部配置request)
- (NSURLSessionTask *)sendRequestBlock:
(id (^)(NSObject *request))requestBlock
progress:(progressBlock)progress
success:(successBlock)success
failure:(failureBlock)failure
发送请求Block(在block内部配置request)
(id (^)(NSObject *request))requestBlock
progress:(progressBlock)progress
success:(successBlock)success
failure:(failureBlock)failure
发送请求Block(在block内部配置request)
typedef void (^successBlock)(id responseObject)
请求成功block
请求成功block
typedef void (^failureBlock) (NSError *error)
请求失败block
请求失败block
typedef void (^responseBlock)(id dataObj, NSError *error)
请求响应block
请求响应block
typedef void (^progressBlock)(NSProgress * progress)
监听进度响应block
监听进度响应block
SMKRequestFileConfig
用来封装上文件数据的模型类
用来封装上文件数据的模型类
继承NSObject
@property (nonatomic, strong) NSData *fileData
文件数据
文件数据
@property (nonatomic, copy) NSString *name
服务器接收参数名
服务器接收参数名
@property (nonatomic, copy) NSString *fileName
文件名
文件名
@property (nonatomic, copy) NSString *mimeType
文件类型
文件类型
+ (instancetype)fileConfigWithfileData:(NSData *)fileData name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType
- (instancetype)initWithfileData:(NSData *)fileData name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType
SMKRequestProtocol
- (id)smk_requestParameters
配置request请求参数
配置request请求参数
- (void)smk_requestConfigures
配置request的路径、请求参数等
配置request的路径、请求参数等
@protocol SMKRequestProtocol <NSObject>
遵守NSObject协议是为了能使用它里面的方法
遵守NSObject协议是为了能使用它里面的方法
SMKMediator
继承NSObject
@property (nonatomic, strong) NSObject<SMKViewModelProtocol> *viewModel
@property (nonatomic, strong) NSObject<SMKViewMangerProtocol> *viewManger;
- (instancetype)initWithViewModel:(id<SMKViewModelProtocol>)viewModel viewManger:(id<SMKViewMangerProtocol>)viewManger;
初始化
初始化
+ (instancetype)mediatorWithViewModel:(id<SMKViewModelProtocol>)viewModel viewManger:(id<SMKViewMangerProtocol>)viewManger;
- (void)noticeViewModelWithInfos:(NSDictionary *)infos;
将infos通知viewModel
将infos通知viewModel
- (void)noticeViewMangerWithInfos:(NSDictionary *)infos;
将infos通知viewMnager
将infos通知viewMnager
SMKViewMangerProtocol
@protocol SMKViewMangerProtocol <NSObject>
- (void)smk_notice
- (void)smk_viewMangerWithSuperView:(UIView *)superView
设置Controller的子视图的管理者为self
设置Controller的子视图的管理者为self
- (void)smk_viewMangerWithSubView:(UIView *)subView
设置subView的管理者为self
设置subView的管理者为self
- (void)smk_viewMangerWithHandleOfSubView:(UIView *)subView info:(NSString *)info
设置添加subView的事件
设置添加subView的事件
- (__kindof UIView *)smk_viewMangerOfSubView
返回viewManger所管理的视图
返回viewManger所管理的视图
- (void)smk_viewMangerWithOtherSubViews:(NSDictionary *)viewInfos
得到其它viewManger所管理的subView,用于自己内部
得到其它viewManger所管理的subView,用于自己内部
- (void)smk_viewMangerWithLayoutSubViews:(void (^)( ))updateBlock
需要重新布局subView时,更改subView的frame或者约束
需要重新布局subView时,更改subView的frame或者约束
- (void)smk_viewMangerWithUpdateLayoutSubViews
使子视图更新到最新的布局约束或者frame
使子视图更新到最新的布局约束或者frame
- (void)smk_viewMangerWithModel:(NSDictionary * (^) ( ))dictBlock
将model数据传递给viewManger
将model数据传递给viewManger
- (ViewEventsBlock)smk_viewMangerWithViewEventBlockOfInfos:(NSDictionary *)infos
处理viewBlock事件
处理viewBlock事件
- (ViewModelInfosBlock)smk_viewMangerWithViewModelBlockOfInfos:(NSDictionary *)infos
处理ViewModelInfosBlock
处理ViewModelInfosBlock
- (ViewMangerInfosBlock)smk_viewMangerWithOtherViewMangerBlockOfInfos:(NSDictionary *)infos
处理ViewMangerInfosBlock
处理ViewMangerInfosBlock
- (void)smk_viewManger:(id)viewManger withInfos:(NSDictionary *)infos
将viewManger中的信息通过代理传递给ViewModel
将viewManger中的信息通过代理传递给ViewModel
typedef void (^ViewEventsBlock)( );
typedef void (^ViewModelInfosBlock)( );
将自己的信息返回给ViewModel的block
将自己的信息返回给ViewModel的block
typedef void (^ViewMangerInfosBlock)( );
将自己的信息返回给ViewManger的block
将自己的信息返回给ViewManger的block
SMKViewModelProtocol
@protocol SMKViewModelProtocol <NSObject>
- (void)smk_notice
- (void)smk_viewModelWithViewController:(UIViewController *)viewController
返回指定viewModel的所引用的控制器
返回指定viewModel的所引用的控制器
- (NSURLSessionTask *)smk_viewModelWithProgress:(progressBlock)progress success:(successBlock)success failure:(failureBlock)failure
加载数据
加载数据
- (void)smk_viewModelWithModelBlcok:(void (^)(id model))modelBlock
传递模型给view
传递模型给view
- (ViewMangerInfosBlock)smk_viewModelWithViewMangerBlockOfInfos:(NSDictionary *)infos
处理ViewMangerInfosBlock
处理ViewMangerInfosBlock
- (ViewModelInfosBlock)smk_viewModelWithOtherViewModelBlockOfInfos:(NSDictionary *)infos
处理ViewModelInfosBlock
处理ViewModelInfosBlock
- (void)smk_viewModel:(id)viewModel withInfos:(NSDictionary *)infos
将viewModel中的信息通过代理传递给ViewManger
将viewModel中的信息通过代理传递给ViewManger
typedef void (^successBlock)(id responseObject);
请求成功block
请求成功block
typedef void (^failureBlock) (NSError *error);
请求失败block
请求失败block
typedef void (^responseBlock)(id dataObj, NSError *error);
请求响应block
请求响应block
typedef void (^progressBlock)(NSProgress * progress);
监听进度响应block
监听进度响应block
typedef void (^ViewMangerInfosBlock)( );
将自己的信息返回给ViewManger的block
将自己的信息返回给ViewManger的block
typedef void (^ViewModelInfosBlock)( );
将自己的信息返回给ViewModel的block
将自己的信息返回给ViewModel的block
iOS 编码思维路径
苹果版MVC
View不和Mode建立联系,让view可以重用
当view是通用时采用
view的子控件会暴露出来,给控制器使用
如果想要隐藏view的子控件,那么就定义一个set数据的方法出来给控制器调用,实现传输数据
控制器和view建立了联系
控制器和mode建立了联系
MVC变种,
readonley的属性只会生成带_下划线的成员变量
view的子控件会隐藏
view会拥有mode
缺点,view依赖那个mode
其中view的事件处理要传递到外界去,用delegate实现
为什么delegate要遵守NSObject,因为遵守后,可以调用responsselector
MVP
M就是model,v就是view,p就是presentor
MVVM
M就是model
v就是view
vm就是viewmode
mvvm区别mvp就是,mvvm有数据绑定
mvvm刷新数据?
mvvm配合rac使用
mvvm配合fbcontroler使用,是facebook分类
三层架构
界面层
MVC、mvvm、mvp
MVC、mvvm、mvp
只要搭建界面
不需要关注请求地址,交给业务层
业务层
需要什么
数据层
数据来源
四层架构
iOS 课程大纲
iOS 底层课大纲
iOS 越狱课大纲
01 越狱知识点掌握及开发环境搭建
iOS 大神班大纲
iOS开发舆图
0 实例
Collaborative List of open Source Apps
AppCoda.com
Standard University CS 193 P
Raywenderlich.com
01 iOS基础
列表
UITableView
UICollectionView
Expandable Cell
Header
Placeholder
CollectionViewLayout
IGListKit
图表
Charts
图形
Core Graphics
图片
图片处理
PDF
布局
TinyConstraints
AutoLayout
Snapkit
Simplex算法
Flexbox
AsyncDisplayKit
Masonry
多媒体
视频
音频
游戏
ARKit
Core Graphics
Core Image
Metal
GPUImage
Scenekit
Image I/O
Sirikit
App Services
硬件
定位(Core Location)
通知
WebView
iCloud
WatchKit
照片
相机
文件管理
手势
键盘
蓝牙
NFC
Force Touch
iBeacon
地图
Apple TV
Email
Passbook
支付
Core Motion
动画
Pop
Spring
Lottie-iOS
UIBezierPath
Core Animation
GUI框架
WebKit框架
Flutter框架
Texture框架
02 iOS系统
Foundation
《Mac OSX and iOS internals》
XNU
dyld
Mach-O
界面
UIViewController
UINavigationController
ScrollView
按钮
Label
表单
登录
菜单
Navigation Bar
Popup
进度条
下拉刷新
Segmented Control
Slider
Splash View
Status Bar
Stepper
Switch
Tab Bar
Picker View
日历
卡片
自定义控件
浮层
弹窗
标签
通知中心
Text
Text Kit
UIPageControl
CoreText
字体
多窗口
向导
03 编程语言
Swift
集合
双精度和布尔
var变量
结构体和类
字符串
正则表达式
枚举
属性
Optional
Mirror
泛型
defer
Never
Result
类型擦除
元编程 Sourcery
动态性
IDE
SwiftNIO
SwiftUI
Combine
Function Builders
命令行
REPL
Docker Swift
时间
数学
Swift源码
Objective-C
字符串
集合
值对象
Method
KVC
KVO
Runtime
Block
反射
Category
架构
SOLID
MVVM
VIPER
CTMediator
路由
消息总线
设计模式
工厂方法
单例方法
观察者模式
备忘录模式
外观模式
适配器模式
编程范式
函数式编程
面向协议编程
响应式编程
ReactnativeCocoa
RXSwift
代码规范
SwiftSyntax
SwiftLint
OCLint
Modern Code Review:A case at Google
04 开发工具
Xcode
Instrument
编译
LLVM
Driver
Clang
LLVM IR
LLVM Backend
链接器
静态分析
方法跟踪
Favx Pas
LSP(language Server Protocol)
Coverity
Inter
CLang Static Analyzer
Libclang
LibTooling
Clang Plugin
AST matchers
包管理
Swift Package Manager
CocoaPods
Carthage
05 开发完成
调试
InAppView Debugger
MTHawkeye
DTrace
LLDB
Injection for xcode
Injectionlll
Lookin
持续交付
Jenkins
Fastlane
Venom
EasyBox
Build
CMake
App Store
App Store Connect
项目管理
包大小
测试
单元测试
BDD
TDD
XCTest
Kiwi
Specta
Expecta
OCMock
OHHTTPStubs
A/B测试
界面测试
代码覆盖率
自动化测试
06 上线后
APM
DoraemonKit
Matrix
GodEye
卡顿监控
内存监控
启动速度(fishhook、Messier)
电量
听云
友盟
Fabric
崩溃
PLCrash reporter
KSCrash
Crash日志解析
Watch Dog
后台崩溃
野指针
bugly.qq.com
日志
Swifty Beaver
Cocoa Lvmberjack
XLog
Logan
无侵入埋点
动态化
Wax Patch
JSPatch
DynamicCocoa
OCS
MABlockClosure
Aspects
OCEval
Libffi
Hybird
JSBridge
React Native
Weex
07 计算机基础
汇编
PC Assembly Book
x64 Cheat Sheet
Stantand CSIOT Givd to x86-64
Calling Convention
Assembly
ARM64
C in ASM (ARM64)系列
mikeash.com
Dissecting objc.msgSend on ARM64
算法
链表
KMP
二叉树
二叉排序树
AVL二叉平衡树
红黑树
B树
队列
Recursion
动态规划
Greedy
NP Complete
Bit Manipulation
Big-O notation
数据结构
Stack
Array
List
Map
Multimap
Set
Multiset
Graph
Queue
内存
循环引用
FBMemory Profiler
内存管理
内存布局
TaggectPointer
引用计数
弱引用表
ARC、MRC
自动释放池
内存映射
MMKV
OOM
多线程
NSThread
GCD
@synchronized
NSLock
NSRecursiveLock
pthread_mutex
OSSpinLock
后台模式
RunLoop
CFRunLoop
NSRunLoop
NSMachPort
NSPortMessage
Matrix
Mach-O
08 通用知识
渲染
Core Animation Pipeline
VSync与双缓冲机制
Skia
Vulkan
Blink Rendering
OpenGL ES
GPU
数据库
SQLite Swift
Core Data
Realm
FMDB
WCDB
归档
网络
TCP
UDP
DNS解析
HTTP协议
URLProtocol
NetworkExtension
网络安全
Charles
mitmproxy
Wireshark
Multipart
Alamofire
NSURLSession
OAuth2
OAuthSwift
GraphQL
数据表示
JSON
JSONModel
YYModel
JSONDecoder
Simdjson
lkiqaJSON
CVS
XML
09 专有知识
智能
TensorFlow
BNNS
MPSCNN
Create ML
Core ML
MLKit
图像识别
YOLO
安全
iOS代码签名
CyptoSwift
KeychainAccess
Valet
RNCryptor
混淆加固
Encryption
服务侧
Vapor
Petect
Kitura
dockSwiftOnARM
小程序
taro
区块链
10 视野
iOS SDK
UIKit
UIViewController
UICollectionViewController
一个专门管理集合视图的视图控制器。
UICollectionView
一个管理数据项的有序集合的对象,并使用可定制的布局来展示它们。
Drawing
使用颜色、渲染器、绘制路径、
字符串和阴影来配置你的应用程序的绘图环境。
使用颜色、渲染器、绘制路径、
字符串和阴影来配置你的应用程序的绘图环境。
Color
UIColor
Drawing Contexts
使用渲染器将一组程序化的
绘图命令转化为位图或PDF图像。
使用渲染器将一组程序化的
绘图命令转化为位图或PDF图像。
UIGraphicsRenderer
UIGraphicsRendererContext
Paths
UIBezierPath
一个由直线和曲线段组成的路径,
你可以在你的自定义视图中呈现。
一个由直线和曲线段组成的路径,
你可以在你的自定义视图中呈现。
介绍
Inherits From
NSObject
NSObject
Conforms To
NSCopying
NSSecureCoding
NSCopying
NSSecureCoding
@interface UIBezierPath : NSObject
Creating a Bézier Path
bezierPath
+ (instancetype)bezierPath;
bezierPathWithRect:
+ (instancetype)bezierPathWithRect:(CGRect)rect;
bezierPathWithOvalInRect:
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
bezierPathWithRoundedRect:cornerRadius:
init
initWithCoder:
Constructing a Path
构建路径
构建路径
moveToPoint:
将路径的当前点移动到指定的位置。
- (void)moveToPoint:(CGPoint)point;
addLineToPoint:
在路径上添加一条直线。
- (void)addLineToPoint:(CGPoint)point;
线段的目的点,在当前坐标系中指定。
本方法创建一个以当前点为起点,以point参数指定的点为终点的直线段。
添加线段后,本方法将当前点更新为point中的值。
添加线段后,本方法将当前点更新为point中的值。
在调用本方法之前,必须设置路径的当前点
(使用moveToPoint:方法或通过之前创建的直线或曲线段)。
如果路径为空,本方法不做任何操作。
(使用moveToPoint:方法或通过之前创建的直线或曲线段)。
如果路径为空,本方法不做任何操作。
appendPath:
将指定路径对象的内容添加到路径中。
- (void)appendPath:(UIBezierPath *)bezierPath;
removeAllPoints
currentPoint
@property(nonatomic, readonly) CGPoint currentPoint;
图形路径中的当前点。
closePath
- (void)closePath;
关闭最近的子路径。
这个方法通过在子路径的第一点和最后一点之间创建一个线段来关闭当前的子路径。此方法随后将当前点更新到新创建的线段的末端,也就是现在关闭的子路径中的第一个点。
Drawing Paths
绘制路径
绘制路径
fill
使用当前的绘图属性来绘制路径所包围的区域。
本方法使用当前的填充颜色和绘图属性来填充路径。如果路径中包含任何打开的子路径,本方法会在绘制填充区域之前隐式关闭它们。
绘制的区域包括路径线本身的像素,但不包括路径线。对于线宽较大的路径,这可能会导致填充区域和被描边的路径(其本身以路径线为中心)之间的重叠。
此方法在绘制前自动保存当前图形状态,并在绘制完成后恢复该状态,因此您不必自己保存图形状态。
fillWithBlendMode:alpha:
使用指定的混合模式和透明度值来绘制路径所包围的区域。
Accessing Drawing Properties
访问图纸属性
访问图纸属性
lineWidth
路径的线宽。
线宽定义了接收器的描边路径的厚度。宽度为0被解释为可以在特定设备上渲染的最薄的线。实际渲染的线宽可能与指定的宽度相差2个设备像素,这取决于线相对于像素网格的位置和当前的抗锯齿设置。线条的宽度还可能受到活动图形上下文的当前变换矩阵中指定的缩放因子的影响。
默认线宽为1.0。
默认线宽为1.0。
lineCapStyle
lineJoinStyle
笔画路径的连接段之间的连接形状。
miterLimit
有助于避免在连接线段之间的交界处出现尖峰的极限值。
@property(nonatomic) CGFloat miterLimit;
flatness
决定曲线路径段渲染精度的因素。
Specifying Clipping Paths
指定剪接路径
指定剪接路径
addClip
UIRectFill
Strings
UIBaselineAdjustment
Core Animation
Layer Basics
CALayer
介绍
Inherits From
NSObject
NSObject
Conforms To
CAMediaTiming
NSSecureCoding
CAMediaTiming
NSSecureCoding
Creating a Layer
layer
+ (instancetype)layer;
Modifying the Layer Geometry
修改层的几何形状
修改层的几何形状
frame
@property CGRect frame;
Modifying the Layer’s Appearance
修改图层的外观
修改图层的外观
mask
一个可选的图层,其alpha通道用于掩盖图层的内容。
@property(strong) __kindof CALayer *mask;
此属性的默认值为零。当配置蒙版时,记得设置蒙版图层的大小和位置,以确保它与被蒙版的图层正确对齐。
您分配给此属性的图层必须没有超级图层。如果有的话,行为是未定义的。
Text, Shapes, and Gradients
CATextLayer
一个提供简单的文本布局和渲染纯文本或属性字符串的层。
一个提供简单的文本布局和渲染纯文本或属性字符串的层。
介绍
@interface CATextLayer : CALayer
Inherits From
CALayer
NSObject
CALayer
NSObject
如果要实现UILabel的效果,就要使用attribute的属性
Getting and Setting the Text
string
@property(copy) id string;
文本必须是NSString或NSAttributedString的实例。默认值为nil。
Text Visual Properties
font
@property CFTypeRef font;
用于渲染接收方文字的字体。
可以是 CTFontRef、CGFontRef、NSFont 实例(仅适用于 macOS)或命名字体的字符串。
在iOS中,你不能将UIFont对象分配给此属性。默认值为 Helvetica。
字体属性仅在字符串属性不是 NSAttributedString 时使用。
如果字体属性是CTFontRef、CGFontRef或NSFont的实例,则忽略该属性的字体大小。
fontSize
fontSize 属性仅在字符串属性不是 NSAttributedString 时使用。
foregroundColor
@property CGColorRef foregroundColor;
Text Alignment and Truncation
wrapped
alignmentMode
truncationMode
决定如何截断文本以适应接收者的边界。
@property(copy) CATextLayerTruncationMode truncationMode;
可能的值在 Truncation 模式中有所描述,默认为 kCATruncationNone。默认值为kCATruncationNone。
CAShapeLayer
A layer that draws a cubic Bezier spline in its coordinate space.
一个在其坐标空间中绘制立方贝塞尔花键的图层。
A layer that draws a cubic Bezier spline in its coordinate space.
一个在其坐标空间中绘制立方贝塞尔花键的图层。
介绍
Inherits From
CALayer
NSObject
CALayer
NSObject
@interface CAShapeLayer : CALayer
只有CAShpeLayer才有Path属性吗?
Specifying the Shape Path
path
定义要渲染的形状的路径。可动画化。
@property CGPathRef path;
与大多数可动画属性不同,路径(与所有的CGPathRef可动画属性一样)不支持隐式动画。
不支持隐式动画,就是说,改变这个属性,不会自动执行动画
不支持隐式动画,就是说,改变这个属性,不会自动执行动画
路径对象可以使用 CAPropertyAnimation 的任何一个具体的子类来制作动画。路径将作为 "在线 "点的线性混合进行插值;"离线 "点可以进行非线性插值(例如,为了保持曲线导数的连续性)。如果两条路径的控制点或控制段的数量不同,那么结果将无法定义。如果路径延伸到图层边界之外,它不会自动被剪切到图层,只有当正常的图层遮蔽规则导致这样的情况时才会被剪切。
此属性的默认值为nil。
此属性的默认值为nil。
Accessing Shape Style Properties
fillColor
用于填充形状路径的颜色。可动画化。
@property CGColorRef fillColor;
strokeColor
用来描边形状的路径的颜色。可动画化。
将strokeColor设置为nil,则不会显示任何笔触。
默认为nil。
默认为nil。
lineCap
指定形状的路径的线帽样式。
@property(copy) CAShapeLayerLineCap lineCap;
strokeStart
开始划动路径的相对位置。可动画化。
@property CGFloat strokeStart;
strokeEnd
fillRule
填充形状的路径时使用的填充规则。
@property(copy) CAShapeLayerFillRule fillRule;
lineWidth
指定形状路径的线宽。可动画化。
CAGradientLayer
在其背景色上绘制颜色渐变的图层,填充图层的形状(包括圆角)。
在其背景色上绘制颜色渐变的图层,填充图层的形状(包括圆角)。
介绍
Inherits From
CALayer
NSObject
CALayer
NSObject
Gradient Style Properties
colors
一个CGColorRef对象的数组,定义了每个渐变停止的颜色。可动画化。
@property(copy) NSArray *colors;
locations
一个可选的NSNumber对象数组,定义每个梯度停止的位置。可动画化。
@property(copy) NSArray<NSNumber *> *locations;
梯度停顿值指定为0和1之间的值,这些值必须是单调增加的。如果为零,则在整个范围内均匀分布停止值。默认值为零。
渲染时,在插值之前,颜色会被映射到输出颜色空间。
渲染时,在插值之前,颜色会被映射到输出颜色空间。
endPoint
在图层的坐标空间中绘制梯度的终点。可动画化。
@property CGPoint endPoint;
终点对应于梯度的最后一站。该点是在单位坐标空间中定义的,然后在绘制时被映射到图层的边界矩形上。
默认值为(0.5,1.0)。
默认值为(0.5,1.0)。
startPoint
在图层的坐标空间中绘制梯度的起始点。可动画化。
@property CGPoint startPoint;
起始点对应于梯度的第一站。该点在单位坐标空间中定义,然后在绘制时被映射到图层的边界矩形上。
默认值为(0.5,0.0)。
默认值为(0.5,0.0)。
type
@property(copy) CAGradientLayerType type;
Defaults to kCAGradientLayerAxial.
图层绘制的渐变样式。
Animation
CAAnimation
CABasicAnimation
CAPropertyAnimation
CAKeyframeAnimation
CASpringAnimation
CATransition
Animation Groups
CAAnimationGroup
iOS 越狱
iOS 越狱步骤
界面分析
代码分析
动态调试
代码编写
Android
Flutter
Flutter-自建仓库学习资料
Flutter控件
ScrollController
监听滚动偏移量
Flutter_app创建过程
1.main.dart启动控件
Flutter启动类main.dart的函数
2.main.dart下的第一个封装的widget
可以是一个又封装了一层的wiget,
封装这一个主要是为了方便改什么呢?
封装这一个主要是为了方便改什么呢?
3.封装一个有底部导航和顶部导航的widget
一般这个widget接触到app所有的主widget,
就是说一个tab一个主widget
就是说一个tab一个主widget
Flutter开发体系
6 开源App
awesome-flutter
前端
项目管理
互联网开发流程
1.外包项目开发流程参考
1.意向接触阶段
与客户初步接洽,了解客户大体想法
我方资质、能力、业务范围、相关产品画册等介绍
与客户交流,梳理出大概产品思路
2.需求阶段
双方达成合作意向后
选定项目经理
根据与客户逐步接洽,梳理出客户需求
业务逻辑
涉及平台
web网站
微信公众平台
终端平台建设
android
ios
后端管理平台
推送、短信、地图等第三方平台接入
根据客户需求,我方提供初步项目的建设规划文档,包含
需求分析
功能初步探讨
系统流程
服务器建设方案
系统实施方案
项目预算
成功案例
我方资质
3.设计阶段
双方达成共识,签订合同后
创建项目svn
目录分为
document
01 文档
02 UI
03 设计
等
android
因项目需要而定
ios
因项目需要而定
php
因项目需要而定
web
因项目需要而定
项目经理对客户需求进行整理,制作原型
工具
原型设计
justinmind
(现在在用的)
axure
等
思路梳理
xmind
这时有可能需要UI设计出部分效果图
项目经理做好原型反复与客户交流、修改、确认原型后
选定此项目组参与人员
UI设计人员、数据库设计人元、测试人员、移动端android/ios开发人员、后端开发人员、web开发人员
给相应人员分配svn权限,大家下载项目资料,大致了解项目情况
项目经理召开项目第一次项目例会
以上人员全部需要参加
项目经理通过原型讲解项目
项目业务需求、功能实现设计
参与人员对项目进行了解、理解后
有疑问、建议的话可以随便提出
项目经理对大家疑问进行解答
对建议进行讨论,合理的话进行采纳
大家对项目有统一的认识后
UI设计
根据原型进行UI页面设计
数据库设计人员
进行数据库设计
项目经理
编辑json交互文档
测试人员
根据原型编写测试用例
后台、移动端开发人员
根据项目实际情况,可先搭建部分项目框架
项目经理做开发规划
工期倒推节点设定
4.实施阶段
UI设计
将效果图、切图文件传到svn相应目录
android端开发人员
到svn项目目录clone自己需要的素材
编写页面
后端开发人员
创建数据库
编写后台
根据json文档,编写json文件
添加到json网站上
移动端与后端联调json
根据时间规定,完成beta1版本,提交测试人员测试
测试人员按测试用例进行测试,没有问题后
ios人员进入开发
每周一开项目例会
进行
上周总结
上周工作情况,按没按上周的本周计划完成
如果没完成,没完成的原因是什么
交付物是什么
本周计划
制定本周工作计划
经过UI设计、测试、移动端开发人员、后端开发人员、项目经理,协同合作
完成项目一期要求
android
编写更新内容
提交各大android应用市场
ios
编写更新内容
提交app store审核
5.落地阶段
对项目进行交付使用
试运行后调优
自己产品的话
安排运营部进行网宣工作
整理市场、运营部门的内容反馈
根据市场实际反馈情况进行下一期的功能梳理
6.项目经理进行总结
实际完成情况与最初计划有什么差异
为什么有这样的差异
以后如何避免
如何更加准确的对项目进行预估
2.互联网开发流程【容老师】
集成方式
按业务集成
1.需求问题暴露的比较晚
2.业务A-代码仓库
3.业务B-代码仓库
4.网路库
版本迭代
按需求集成
1.需求完成就合码
2.需求delay
1.合码回退
团队规模
产品形态
1.平台型的产品
1.腾讯的手Q
2.美团
3.【发版节奏】一周发一次车
需求开发完就发版
2.独立的产品【非平台类】
1.需要对接不同的业务方
2.规模都是100人以上
3.腾讯的微信
4.【发版节奏】按需求迭代,需求完成就完成
5.[发版节奏]视频类的app不赶时间
6.[没有时间节点]淘宝类的
7.一年定发版周期
工程形态
非组件化
组件化
埋点会涉及多个项目的改动
组件化的研发效率要做到没有组件化的研发效率
组件化带来的收益
编译速度提升
快速组成一个app
组件化带来的问题
二进制debug的效率
职责划分
业务团队
性能优化团队
APM
纯业务团队
UGC需求
政府需求
基础团队
基础平台
研发平台
网路库
版本号的变更
图片库
研发怎么接到一个需求
需求评审
产品内部评审
设计内部评审
用例的评审
Test case库
测试人员维护
跑的不好的公司是研发、产品、测试,开会给产品补齐需求
开了会知道怎么测试产品
软件灰度测试
1.Testflight灰度
1万多的量
安卓的灰度量非常大
2.越狱灰度
3.灰度版本
灰度版本1
问题多
灰度版本2
一般发个3到4个版本灰度
3.如何做好持续交付
1.持续交付到底有什么价值?
你了解持续交付吗?
刘汶总结:
持续交付的价值就是快速适应市场环境,提高自己产品的竞争力
持续集成、持续交付和持续部署的关系
持续集成
我们通常会把软件研发工作拆解,拆分成不同模块或不同团队后进行编码,编码完成后,进行集成构建和测试。这个从编码到构建再到测试的反复持续过程,就叫作“持续集成”。
1.编码
1.开发人员写代码
2.构建
1.开发人员打包、发布
3.测试
1.测试人员进行测试
持续集成过度到持续交付
这个在“持续集成”之后,获取外部对软件的反馈再通过“持续集成”进行优化的过程就叫作“持续交付”,它是“持续集成”的自然延续。
“持续集成”一旦完成,则代表产品处在一个可交付状态,但并不代表这是最优状态,还需要根据外部使用者的反馈逐步优化。当然这里的使用者并不一定是真正的用户,还可能是测试人员、产品人员、用户体验工程师、安全工程师、企业领导等等。
获取外部对软件的反馈,然后又持续集成,这个过程叫持续交付
持续部署
传统安装型软件,要现场调试,要用户购买等等,其难度可想而知。即使是可达度最高的互联网应用,由于生产环境的多样性(各种软件安装,配置等)、架构的复杂性(分布式,微服务)、影响的广泛性(需要灰度发布)等等,就算产品已是待交付的状态,要真正达到用户可用的标准,还有大量的问题需要解决。
而“持续部署”就是将可交付产品,快速且安全地交付用户使用的一套方法和系统,它是“持续交付”的最后“一公里”。
刘汶总结:持续部署就是将可以交付的产品快速的交付给用户使用
可见,“持续交付”是一个承上启下的过程,它使“持续集成”有了实际业务价值,形成了闭环,而又为将来达到“持续部署”的高级目标做好了铺垫。
虽然从概念上你可以这样理解,但从实践和我个人多年的经验来说,往往是从“持续部署”(自动化发布)开始推进“持续交付”,这才是一条优选的路径。这部分内容我会在后续文章中详细介绍。
持续交付的显性价值
持续交付也通常以“发布流水线”的方式来解释,即研发团队从开发,到测试,再到部署,最终将产品交付给最终用户使用的过程。如下图:
虽然持续交付着重打造的是发布流水线的部分,但它所要达到的目标是在“最终用户”和“研发团队”之间建立紧密的反馈环:通过持续交付新的软件版本,以验证新想法和软件改动的正确性,并衡量这些改动对软件价值的影响。
这里说的“软件价值”,说白了就是收入、日活、GMV 等 KPI 指标了。
通常我们在实施持续交付后,都能够做到在保证交付质量的前提下,加快交付速度,从而更快地得到市场反馈,引领产品的方向,最终达到扩大收益的目的。
在互联网应用盛行、速度为王的今天,持续交付的价值更是被突显出来。持续交付的能力,正成为评定一家互联网公司研发能力的重要指标。
持续交付的隐性价值
如果你是 CTO 或者是一个较大规模研发团队的管理者
你是不是时常困扰于技术选型的问题?
技术选型最大的难点在于影响大,又难以验证(或者验证效率低下)。而造成这些困境的绝大多数原因是没有合适的测试环境,比如环境差异造成测试数据缺乏说服力,又比如缺少隔离环境造成服务冲突等等。而这正是持续交付的用武之地。
持续交付的实施,将全面改善企业对测试环境的管理方法,使得环境管理更合理、更自由。我也将在后续章节里介绍如何做好环境管理。
你是不是经常头痛于已制定的标准难以落地?
标准、规范、流程的落地,都需要载体,而最好的载体就是平台工具。而持续交付是一整套平台工具的落地,几乎涵盖了研发的整个生命周期,是天然的、最佳的载体。
另外,持续交付的落地本身就伴随着各类标准、规范、流程的制定和实施,可以说两者相互依存,是非常好的管理思想落地方案。
你是不是时常考虑如何提高跨部门协作的效率?
我看到的每一个持续交付实施团队,都可以说是最厉害的“拆墙大队”,拆的就是各个研发协作部门间的“隔离墙”。
持续交付能够向各个协作部门输出统一的标准、流程和工具,提升沟通效率;并且通过大量的自动化,进一步提升各部门工作效率;还可以快速集成,把各个分散的团队,无论是横向的业务研发团队,还是纵向的技术框架团队,紧紧地联系在一起,共同进退。
你是不是担心“黑天鹅”的降临?
既然叫“黑天鹅”,那就是说明它的产生有一定的必然性。正应了一句老话“是福不是祸,是祸躲不过”,既然躲不过,那就解决它呗。其实任何故障都有一个天敌,叫作:快速恢复。
假设,所有的故障都可以在 3 分钟内恢复,你是不是觉得天下无敌了。那恢复故障最快、最有效的手段又是什么呢?当然就是回滚(或重新部署)了,而这正是持续交付所包含和着力打造的能力之一。
如果你是 Team Leader
你一定希望团队的知识能够传承。
互联网公司的人才流动之频繁已经远远超过了你我的想象。人来人往,如何将知识传承下来呢?其实在这方面,持续交付也能为团队提供很多帮助。
首先,持续交付将团队赖以生存的工作流程进行了固化;其次,利用代码静态检查等工具,能够很好地传承团队多年来的代码规范,并作为检查项进行自动化校验;再次,自动化测试的脚本,同样是团队经验的产物。
你一定希望团队专注于业务而非工程。
目前越来越多的公司或研发组织意识到,持续交付体系也如同中间件一样,能够从日常的业务研发工作中抽象出来,其不同只在于中间件解决架构问题,而持续交付解决工程问题。
这样研发团队能够全力应付业务的需求,而不用总是重复奔波于一些烦人且耗时的工程问题,比如安装测试机、准备编译服务器等等。
你一定希望以一个较平稳的节奏持续工作。
虽然在实施持续交付的初期,团队为了适应新的流程和工具,会有一定的效率下降,但之后在自动化的帮助下,团队效率会有一个明显的提升并逐渐稳定下来。
持续交付就是这样通过稳固的流程、自动化的工具和公开而真实的数据,来避免发布前夕容易发生的“死亡行军”式开发阶段。
如果你是产品经理
你应该是产品真正的第一个用户。
持续交付不仅仅是可以保证每一个变化都能及时得到测试以及反馈,更多的是解决测试与实际发布时存在差异的问题。
产品人员再也不会陷入“为什么用户端运行的结果,和在测试环境中的不一致”这样的窘境,他们将真正成为第一个用户,而不再是最后一个 QA。
你应该完全知悉当前的进度和质量。
作为产品人员,你是不是一直有这样的感觉:和研发团队之间总有一扇墙,程序员们似乎并不乐意告诉产品人员项目的真相;而最终总有这样那样的理由造成延期,产品人员往往无话可说。
那么,持续交付就能够实时地反应当前的开发情况,从而帮助产品人员决策和调整。
你的产品应该随时能发布。
计划永远赶不上变化,任何产品人员都希望自己的产品能够随时处于可发布状态。这样就能灵活地交付已完成的功能,迎合市场或业务的需要。
本质上,做到代码上线和业务上线的解耦分离,这也正是持续交付方法论强调的一个重点。
如果你是一个程序员
你可以通过对持续交付的学习,进一步加强自己对整个软件工程的认识。
持续交付涵盖了软件交付端到端的整个周期,其覆盖面不仅仅包括编码,还包括:设计、测试、部署、运维、运营等等。
如果你对自己的发展有更高的要求,那么你就应该学习一下持续交付的内容,它能让你看到更多与编码有关的其他东西,比如不同的编码方式等;也能让你站在更高的角度去看待自己的工作:研发效率的提高往往不是个人能力的提高,而是集体协同效率的提高。
你可以利用持续交付的工具或最佳实践,提高自己的工作效率和质量。
随着持续交付的流行,其配套的实践和工具也层出不穷。如果你玩过 ping-pong 式的结对编程(A 写测试,B 写实现,然后 B 写下一个测试,A 写重构和实现),你一定会觉得编程如此轻松有趣,而这种 TDD 的方式也很好的保证了代码质量。
你可以参与到持续交付实施中去,享受为其他程序员提供效率工具的挑战和乐趣。
试想一下,如果你是一个出租车司机,而你的乘客却是舒马赫(F1 世界冠军),此时你开车的压力会有多大。其实参与到持续交付的实施中也是一样,因为你正在用程序员的方式改造程序员的工作习惯,为程序员提供工具。
虽然挑战和压力巨大,但这又是如此有趣,你将会站在另一个高度去看你曾经的工作,不想试试吗?
如何评估持续交付的价值
我跟你说了这么多持续交付的价值,那如何评估它呢?这是一个非常难的问题,我自己每年在绩效考评时也都会问自己这个问题:我到底应该怎么给老板汇报呢?我可以量化持续交付的价值吗?
首先,你一定会说,我可以衡量产品的交付速度是否变快了。但是,实际情况下影响产品交付速度的因素实在太多,虽然我们一定知道持续交付有积极作用,但到底占比是多少呢?好像非常模糊,难以回答。
然后,你又想到,我们可以衡量各个自动化过程的速度是否变快了,比如:编译速度、发布速度、回滚速度、自动化测试速度等等。
是的,这些指标确实很好地反应了持续交付的价值,但总觉得这些并不是全部,持续交付的标准化、推行的新流程、改革的环境治理架构,好像都没有体现出来。
那到底应该怎么评估持续交付的价值呢?这里和你分享一下我在携程是怎么解决这个问题的。
消灭不可持续点
不可持续点
我除了会评估一些常规的 KPI 外,更多地会换一种思考方式。既然很难量化持续交付的价值,那么我们就具象化,来看看整个工程生命周期中有多少被开发人员诟病,或者阻碍开发人员自助处理的问题点 ,即“不可持续点”:
开发不能按需产生隔离的测试环境;
生产代码回滚后,要手工处理代码分支;
预发布(Staging)流量要能自动分离,以便预发布测试。
在携程,我们会将所有的“不可持续点”进行记录和分解,通过 OKR 的考评方式,将消灭这些点作为目标,拆解出来的可行动点,作为关键结果,以这样的方式来完成绩效考评。
虽然,有些“不可持续点”已经超越了一般传统持续交付的概念,甚至有些已经超越了纯技术改进的范畴,但是持续交付仍会一直关注于消灭这些“不可持续点”。
So what,我们就是要持续交付我们的价值!
2.影响持续交付的因素有哪些?
组织和文化因素
第一个层次:紧密配合,这是组织发展,部门合作的基础。
一般企业都会按照职能划分部门。不同的职能产生不同的角色;不同的角色拥有不同的资源;不同的资源又产生不同的工作方式。这些不同的部门紧密配合,协同工作于共同的目标,就能达到成效。
第二个层次:集思广益,这就需要组织内各个不同部门,或不同职能的角色,跳出自身的“舒适区”。
除思考和解决本身职能的问题外,各部门还要为达到组织的共同目标,通盘考虑和解决所遇到问题和困难。这个层次需要增加组织的透明度,需要接受互相批评和帮助。
第三个层次:自我驱动,是理想中的完美组织形式
如果第二个层次能够持续地运转,就会形成自我学习、自我驱动的飞轮效应,并且越转越快,它甚至能自发式的预见困难,并自驱动解决问题。
这三个层次看起是不是有点眼熟,和我在上一篇文章中讲到的持续集成的三个层次:
分模块编码;
整体集成;
实现以上两个过程的自动化,并形成闭环;
那么,在形成理想组织的实际执行中会遇到哪些问题呢?
好像是一样的。真是有趣,持续交付其实也是帮企业建立更好的组织形式的一种方法。
一般软件企业与交付有关的研发部门包括四个:产品、开发、测试和运维。而这四个部门天然地形成了一个生产流水线,所以形成理想组织的第一层次紧密配合,基本没什么问题。
但是,要达到第二层次集思广益的难度,往往就很大。因为,每个部门有自身的利益,以及自己的工作方式和目标。
比如,产品人员和测试人员就是一对矛盾体:产品人员希望产品尽快上线,而测试人员则希望多留时间进行更完整的测试。
又比如,开发人员和运维人员也经常矛盾:开发人员希望能有完全权限,而运维人员却控制着生产的 root。
从各自的小目标的角度看,这些矛盾是正常的。但是,产品、开发、测试和运维这些部门的小目标往往就是实施持续交付的阻碍,只有它们把眼光放到更高地持续交付可用的产品上,有了共同的目标,问题才会迎刃而解。
那么,靠各个部门自己能解决这个问题吗,其实很难。组织的问题,还是需要通过组织变革来解决。通常我们会采用以下三种方案:
成立项目管理办公室(Project Manage Office,简称 PMO)这样的监督型组织,帮助持续交付落地;
独立建立工程效能部门,全面负责包括持续交付在内的研发效率提升工作;
使用敏捷形式,如 Scrum,打破职能部门间的“隔离墙”,以产品的形式组织团队,各团队自行推进持续交付 。
当然,这三种方案各有利弊。比如:
成立项目管理办公室,虽然会带来非常强大的项目推进力,但它往往需要通过流程把控进行监督,这样就很有可能把流程变得更加复杂;
而独立的工程效能部门,虽然能最大化地去做好持续交付工作,但其研发成本的投入也是需要考虑的,小团队的话,就不太适用了;
敏捷形式是比较适合中小团队的一种组织变革方式,但对个人能力的要求也会比较高,而且往往需要一个很长时间的磨合才能见效。
所以,你需要根据当前组织的情况来选择。总而言之,持续交付必须有与其相适应的组织和文化,否则将很难实施。
谈到组织,你是不是一下就想到了部门划分,跨部门合作等?的确,这就是我要和你讲的第一个影响因素。因为“持续交付“一定是整个组织层面的事情,是跨部门合作的产物,所以组织和文化因素,是要首先考虑的问题。
什么样的组织文化,才是“持续交付”成长的沃土(当然这也是定义好的组织的标准),我把它分成了三个层次:
流程因素
要说持续交付对企业和组织改变最多的是什么,那么一定是流程。
持续交付一定会打破的这三类流程是:
耗时较长的流程。比如,一个功能的研发迭代周期为 5 天,而其中有一个上线审核流程,需要花费 3 天时间,那这个流程就严重影响了持续交付,必须被打破。
完全人工类的流程。 完全人工操作的流程,一般效率低下,且质量难以保证,持续交付的逐步深入会通过自动化替代这些人工流程的存在。
信息报备类的流程。 持续交付过程中同样会产生各种信息流,这些信息有些需要广播,有些需要定点传递。实施持续交付后,这些信息报备类的流程一定会通过异步消息等方式进行改造。
其中,如何对待审批流程是重点。
在持续交付过程中,其实最让你头痛的应该是一些审批流程。这些流程既然叫做审批,那就代表着授权与责任,代表着严谨与严肃,因此也一定有其存在的价值和意义,不能轻易被去除或打破。
但是,你我都知道,审批往往指的是由人进行审核和批准,既是一个全人工流程,又是一个信息流转类流程。那么如何打破它呢?同样,也有几种思路:
该审批流程是否确实需要,如果能够通过系统来保证,则可以去除;
该审批流程是否可以从事前审批转化为事后审核;
该审批流程是否可以被简化。
但是,每家公司的流程都不太一样,所以我的这几个思路并不一定是放诸四海而皆准,但我希望你可以借鉴,或者从中学习到一些新的思路,并结合你自己的情况进行合理调整。
相对于组织文化和流程因素,架构是真正和技术相关的因素,也是我要和你重点分享的内容。
架构因素
第一,系统架构
对单体架构来说:
整个应用使用一个代码仓库,在系统简单的情况下,因为管理简单,可以快速简单地做到持续集成;但是一旦系统复杂起来,仓库就会越变越大,开发团队也会越来越大,多团队维护一个代码仓库简直就是噩梦,会产生大量的冲突;而且持续集成的编译时间也会随着仓库变大而变长,团队再也承受不起一次编译几十分钟,结果最终失败的痛苦。
应用变复杂后,测试需要全回归,因为不管多么小的功能变更,都会引起整个应用的重新编译和打包。即使在有高覆盖率的自动化测试的帮助下,测试所要花费的时间成本仍旧巨大,且错误成本昂贵。
在应用比较小的情况下,可以做到单机部署,简单直接,这有利于持续交付;但是一旦应用复杂起来,每次部署的代价也变得越来越高,这和之前说的构建越来越慢是一个道理。而且部署代价高会直接影响生产稳定性。这显然不是持续交付想要的结果。
总而言之,一个你可以完全驾驭的单体架构应用,是最有容易做到持续交付的,但一旦它变得复杂起来,一切就都会失控。
对 SOA 架构来说:
由于服务的拆分,使得应用的代码管理、构建、测试都变得更轻量,这有利于持续集成的实施。
因为分布式的部署,使得测试环境的治理,测试部署变得非常复杂,这里就需要持续交付过程中考虑服务与服务间的依赖,环境的隔离等等。
一些新技术和组件的引入,比如服务发现、配置中心、路由、网关等,使得持续交付过程中不得不去考虑这些中间件的适配。
总体来说,SOA 架构要做到持续交付比单体架构要难得多。但也正因架构解耦造成的分散化开发问题,持续集成、持续交付能够在这样的架构下发挥更大的威力。
对微服务架构来说:
其实,微服务架构是一种 SOA 架构的演化,它给持续交付带来的影响和挑战也基本与 SOA 架构一致。
当然,如果你采用容器技术来承载你的微服务架构,就另当别论了,这完全是一个持续交付全新的领域,这部分内容我将在后续文章中跟你分享。
系统架构指系统的组成结构,它决定了系统的运行模式,层次结构,调用关系等。我们通常会遇到的系统架构包括:
单体架构,一个部署包,包含了应用所有功能;
SOA 架构,面向服务,通过服务间的接口和契约联系;
微服务架构,按业务领域划分为独立的服务单元,可独立部署,松耦合。
第二,部署架构
部署架构指的是,系统在各种环境下的部署方法,验收标准,编排次序等的集合。它将直接影响你持续交付的“最后一公里”。
首先,你需要考虑,是否有统一的部署标准和方式。 在各个环境,不同的设备上,应用的部署方式和标准应该都是一样的,可复用的;除了单个应用以外,最好能做到组织内所有应用的部署方式都是一样的。否则可以想象,每个应用在每个环境上都有不同的部署方式,都要进行持续交付的适配,成本是巨大的。
其次,需要考虑发布的编排次序。 特别是在大集群、多机房的情况下。我们通常会采用金丝雀发布(之后讲到灰度发布时,我会详解这部分内容),或者滚动发布等灰度发布策略。那么就需要持续交付系统或平台能够支持这样的功能了。
再次,是 markdown 与 markup 机制。 为了应用在部署时做到业务无损,我们需要有完善的服务拉入拉出机制来保证。否则每次持续交付都伴随着异常产生,肯定不是大家愿意见到的。
最后,是预热与自检。 持续交付的目的是交付有效的软件。而有些软件在启动后需要处理加载缓存等预热过程,这些也是持续交付所要考虑的关键点,并不能粗暴启动后就认为交付完成了。同理,如何为应用建立统一的自检体系,也就自然成为持续交付的一项内容了。
3.一切的源头,代码分支策略的选择
谈谈主干开发(TBD)
谈谈特性分支开发
第一,Git Flow
第二,GitHub Flow
第三,GitLab Flow
选出最适合的分支策略
国内互联网公司的选择
GitLab 作为最优秀的开源代码平台,被多数互联网大公司(包括阿里、携程和美团点评等)所使用,这些大厂也都采用特性分支开发策略。当然,这些大公司在长期持续交付实践中,会结合各自公司的情况做个性化的定制。
比如,携程公司在 GitHub Flow 的基础上,通过自行研发的集成加速器(Light Merge)和持续交付 Paas 平台,一起完成集成和发布。
再比如,阿里的 AoneFlow,采用的是主干分支、特性分支和发布分支三种分支类型,再加上自行研发的 Aone 协同平台,实现持续交付。
4.会议纪要
后台组
魏建伟
上周
1.切换测试环境
2.小程序支付不了,已进入测试环境
3.提现手续费-未完成提示
4.无忧帮帮关键字替换,基本完成
这周
1.修复禅道上的问题
2.驾驶证界面的开发-20191125上正式机
高国荣
上周
1.阿里云上传图片地址更换
2.微信支付失败解决
3.官网旧页面迁移
这周
1.解决志伟测试的问题
葛平
这周
1.数据库关系梳理
2.缓存机制建立
上周
1.把后台转移到新的服务器上
2.把数据库的脚本写好
建议
1.申请承担小程序代码和后台代码CodeReview职责
2.遇到的服务器接口问题,需要@葛平
向战友
这周
1.后台代理商图片链接
2.企业送平台下单队列的问题
上周
2.前端阿里云OSS的调试
3.代理商管理优惠逻辑的处理
4.推送区分安卓和苹果
App组
吴斌
上周
1.阿里云上传图片地址更换
2.应用市场上架情况的跟踪
3.企业送入驻流程完成部分
这周
1.继续完成企业送入驻流程
建议
1.APP加入热修复功能
2.APP接口接入统计版本号功能
朱柳煌
上周
1.小帮入驻正式服上线
2.禅道问题修复
3.阿里云图片接口更换
这周
1.订单详情界面统一「两天」
2.问题跟进
刘汶
上周
1.用户端企业送入驻的新流程
2.小帮入驻流程着手开发
3.用户端和小帮端弹出的城市合伙人界面调整
这周
1.iOS小帮入驻新流程上架
建议
1.会议内容叙述建议
上周总结
总结得失
总结遇到的问题
这周计划
计划完成的任务
计划完成的时间点
遇到问题
遇到的具体问题,是否有头绪
遇到问题是否需要帮忙
2.会议记录人轮流制度
测试组
曾志伟
上周
1.新增提现手续费,没有测试
2.小帮入驻新界面,后台没有驾驶证
这周
1.用户端用例制作
2.小程序问题测试
产品组
郭颖欣
上周
1.用户端首页的轮播图设计
2.骑手等级的设计稿
3.官网的切图和二维码替换
4.小帮端和用户端logo替换
这周
1.根据调整后的骑手等级原型出新的设计图
徐智斌
上周
1.超时免单原型制作
2.加急单业务原型
3.预约单的规则和原型,初稿结束
4.用户端信息架构图梳理
5.小帮等级业务规则修改
6.无忧帮帮的字的替换工作跟进
这周
1.小帮等级原型变更
2.超时免单、加急、预约单安排时间业务讨论和原型确认
1.这周只抽其中一项
3.今天用户端和小帮端二维码弹框视图修改-20191125完成
建议
1.APP发版最后由产品确认
客服组
孙婷
这周
1.审核企业送和小帮
2.处理小帮返回过来的问题
3.肇庆问题的跟踪
1.无法派单
上周
1.审核企业送和小帮
2.其他的问题
技术心得
学习某个技术,就是先了解这个技术可以实现什么,然后你通过
了解这个技术的每个细节,比如每个SDK的用法,然后你使用它们,
获得你想要的效果。
了解这个技术的每个细节,比如每个SDK的用法,然后你使用它们,
获得你想要的效果。
为什么要看WWDC,就是了解新的技术能够实现什么,
然后让更多的开发者去通过他们的技术实现更多好的
产品。
然后让更多的开发者去通过他们的技术实现更多好的
产品。
除了看WWDC之外,还可以看许多大神已经通过最新的技术
实现的东西,这样你会更加知道这个技术能做的事情会更多
然后你就会去学习怎么使用他们,你掌握的越多,你就越精通
实现的东西,这样你会更加知道这个技术能做的事情会更多
然后你就会去学习怎么使用他们,你掌握的越多,你就越精通
所谓的精通某个语言,就是了解每个SDK之外,你还了解
里面的架构,思想,为什么这么设计,以后的方向,这样
就可以达到精通。
里面的架构,思想,为什么这么设计,以后的方向,这样
就可以达到精通。
收藏
0 条评论
下一页