BlocksKit的使用
一、引言
众所周知Block已被广泛用于iOS编程。它们通常被用作可并发执行的逻辑单元的封装,或者作为事件触发的回调。Block比传统回调函数有2点优势:
- 允许在调用点上下文书写执行逻辑,不用分离函数
- Block可以使用local variables.
基于以上种种优点Cocoa Touch越发支持Block式编程,这点从UIView的各种动画效果可用Block实现就可见一斑。而BlocksKit是对Cocoa Touch Block编程更进一步的支持,它简化了Block编程,发挥Block的相关优势,让更多UIKit类支持Block式编程。BlocksKit是一个block的大杂烩,它给Fundation和UIKit框架里很多的类都做了扩展,可以通过调用相关类的扩展的方法简单的实现一下几个功能:
- 1.通过block传入事件处理函数
- 2.创建动态代理,传入block给想要实现的方法。
- 3.在很多基础的类上增加额外的方法
block可以帮助我们组织独立的代码段,并提高复用性和可读性。而BlocksKit可以很简单的实现block,实现回调,和通信,可以大大减少工作量。
二、下载
1、链接
在浏览器输入https://github.com/zwaldowski/BlocksKit这个连接,进入下面这个页面:
2、下载
点击Clone or download,下载最新的BlocksKit-master;
3、文件结构图
下载下来的文件结构如下图:
v2.2.5
4、BlocksKit目录结构
BlocksKit代码存放在4个目录中分别是Core、DynamicDelegate、MessageUI、UIKit。其中:
- Core 存放Foundation Kit相关的Block category
- DynamicDelegate动态代理(一种事件转发机制)相关代码
- MessageUI 存放MessageUI相关的Block category
- UIKit 存放UIKit相关的Block category
5、Core相关代码分析
Core文件夹下面的代码可以分为如下几个部分:
- 1、容器相关(NSArray、NSDictionary、NSSet、NSIndexSet、NSMutableArray、NSMutableDictionary、NSMutableSet、NSMutableIndexSet)
- 2、关联对象相关
- 3、逻辑执行相关
- 4、KVO相关
- 5、定时器相关
6、导入工程
导入工程有两种方式:
第一种
按照官方文档描述,编译成静态库,添加到自己的文件工程中。第二种
把文件BlocksKit添加到自己工程文件中,然后修改部分.h文件;
修改规则如下:
把所有的
#import <BlocksKit/BlocksKit.h>
修改成
#import "BlocksKit.h"
#import <BlocksKit/BlocksKit.h> #import <BlocksKit/UIActionSheet+BlocksKit.h>
#import <BlocksKit/UIAlertView+BlocksKit.h>
#import <BlocksKit/UIBarButtonItem+BlocksKit.h>
#import <BlocksKit/UIControl+BlocksKit.h>
#import <BlocksKit/UIGestureRecognizer+BlocksKit.h>
#import <BlocksKit/UIImage+BlocksKit.h>
#import <BlocksKit/UIImagePickerController+BlocksKit.h>
#import <BlocksKit/UIPopoverController+BlocksKit.h>
#import <BlocksKit/UITextField+BlocksKit.h>
#import <BlocksKit/UIView+BlocksKit.h>
#import <BlocksKit/UIWebView+BlocksKit.h>
#import <BlocksKit/UITextField+BlocksKit.h>
#import <BlocksKit/UITextView+BlocksKit.h>
#import <BlocksKit/UIImagePickerController+BlocksKit.h>
#import "BlocksKit.h" #import "UIActionSheet+BlocksKit.h"
#import "UIAlertView+BlocksKit.h"
#import "UIBarButtonItem+BlocksKit.h"
#import "UIControl+BlocksKit.h"
#import "UIGestureRecognizer+BlocksKit.h"
#import "UIImage+BlocksKit.h"
#import "UIImagePickerController+BlocksKit.h"
#import "UIPopoverController+BlocksKit.h"
#import "UITextField+BlocksKit.h"
#import "UIView+BlocksKit.h"
#import "UIWebView+BlocksKit.h"
#import "UITextField+BlocksKit.h"
#import "UITextView+BlocksKit.h"
#import "UIImagePickerController+BlocksKit.h"
三、使用
做好上面的步骤之后,在代码中使用就更简单了,使用之前导入相应的头文件
//Foundation框架:
#import "BlocksKit.h"
//UIKit框架:
#import "BlocksKit+UIKit.h"
//QuickLook框架:
#import "BlocksKit+QuickLook.h"
//MessageUI框架:
#import "BlocksKit+MessageUI.h"
然后根据自己的需要调用相关类的扩展方法即可
1、Core举例
(1)、容器相关的BlocksKit
不管是可变容器还是不可变容器,容器相关的BlocksKit代码总体上说是对容器原生block相关函数的封装。容器相关的BlocksKit函数更加接近自然语义,有一种函数式编程和语义编程的感觉。
- 部分不可变容器的BlocksKit声明:
//串行遍历容器中所有元素
- (void)bk_each:(void (^)(id obj))block;
//并发遍历容器中所有元素(不要求容器中元素顺次遍历的时候可以使用此种遍历方式来提高遍历速度)
- (void)bk_apply:(void (^)(id obj))block;
//返回第一个符合block条件(让block返回YES)的对象
- (id)bk_match:(BOOL (^)(id obj))block;
//返回所有符合block条件(让block返回YES)的对象
- (NSArray *)bk_select:(BOOL (^)(id obj))block;
//返回所有!!!不符合block条件(让block返回YES)的对象
- (NSArray *)bk_reject:(BOOL (^)(id obj))block;
//返回对象的block映射数组
- (NSArray *)bk_map:(id (^)(id obj))block; //查看容器是否有符合block条件的对象
//判断是否容器中至少有一个元素符合block条件
- (BOOL)bk_any:(BOOL (^)(id obj))block;
//判断是否容器中所有元素都!!!不符合block条件
- (BOOL)bk_none:(BOOL (^)(id obj))block;
//判断是否容器中所有元素都符合block条件
- (BOOL)bk_all:(BOOL (^)(id obj))block;NSString* str = [arr bk_match:^BOOL(id _Nonnull obj) {
return ((NSString *)obj).length == ;
}];
NSArray* arr_01 = [arr bk_select:^BOOL(id _Nonnull obj) {
return ((NSString *)obj).length == ;
}];
NSArray* arr_02 = [arr bk_reject:^BOOL(id _Nonnull obj) {
return ((NSString *)obj).length == ;
}];
NSLog(@"str = %@",str);
NSLog(@"arr_01 = %@",arr_01);
NSLog(@"arr_02 = %@",arr_02);-- ::12.085 BlocksKitDemoTwo[:] str =
-- ::12.085 BlocksKitDemoTwo[:] arr_01 = (
, )
-- ::12.086 BlocksKitDemoTwo[:] arr_02 = (
, )- 部分可变容器的BlocksKit声明
/** Filters a mutable array to the objects matching the block. @param block A single-argument, BOOL-returning code block.
@see <NSArray(BlocksKit)>bk_reject:
*/
//删除容器中!!!不符合block条件的对象,即只保留符合block条件的对象
- (void)bk_performSelect:(BOOL (^)(id obj))block; //删除容器中符合block条件的对象
- (void)bk_performReject:(BOOL (^)(id obj))block; //容器中的对象变换为自己的block映射对象
- (void)bk_performMap:(id (^)(id obj))block;2)、关联对象相关的BlocksKit
关联对象的作用如下:
在类的定义之外为类增加额外的存储空间。使用关联,我们可以不用修改类的定义而为其对象增加存储空间。这在我们无法访问到类的源码的时候或者是考虑到二进制兼容性的时候是非常有用。关联是基于关键字的,因此,我们可以为任何对象增加任意多的关联,每个都使用不同的关键字即可。关联是可以保证被关联的对象在关联对象的整个生命周期都是可用的(ARC下也不会导致资源不可回收)。
关联对象的例子,在我们的实际项目中的常见用法一般有category中用关联对象定义property,或者使用关联对象绑定一个block。
关联对象相关的BlocksKit是对objc_setAssociatedObject、objc_getAssociatedObject、objc_removeAssociatedObjects这几个原生关联对象函数的封装。主要是封装其其内存管理语义。部分函数声明如下
//@interface NSObject (BKAssociatedObjects) //以OBJC_ASSOCIATION_RETAIN_NONATOMIC方式绑定关联对象
- (void)bk_associateValue:(id)value withKey:(const void *)key;
//以OBJC_ASSOCIATION_COPY_NONATOMIC方式绑定关联对象
- (void)bk_associateCopyOfValue:(id)value withKey:(const void *)key;
//以OBJC_ASSOCIATION_RETAIN方式绑定关联对象
- (void)bk_atomicallyAssociateValue:(id)value withKey:(const void *)key;
//以OBJC_ASSOCIATION_COPY方式绑定关联对象
- (void)bk_atomicallyAssociateCopyOfValue:(id)value withKey:(const void *)key;
//弱绑定
- (void)bk_weaklyAssociateValue:(__autoreleasing id)value withKey:(const void *)key;
//删除所有绑定的关联对象
- (void)bk_removeAllAssociatedObjects;(3)、逻辑执行相关的BlocksKit
所谓逻辑执行,就是Block块执行。逻辑执行相关的BlocksKit是对dispatch_after函数的封装。使其更加符合语义。
主要函数如下
//@interface NSObject (BKBlockExecution) //主线程执行block方法,延迟时间可选
- (BKCancellationToken)bk_performAfterDelay:(NSTimeInterval)delay usingBlock:(void (^)(id obj))block;
//后台线程执行block方法,延迟时间可选
- (BKCancellationToken)bk_performInBackgroundAfterDelay:(NSTimeInterval)delay usingBlock:(void (^)(id obj))block;
//所有执行block相关的方法都是此方法的简化版,该函数在指定的block队列上以指定的时间延迟执行block
- (BKCancellationToken)bk_performOnQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay usingBlock:(void (^)(id obj))block;
//取消block,非常牛逼!!!一般来说一个block加到block queue上是没法取消的,此方法实现了block的取消操作(必须是用BlocksKit投放的block)
+ (void)bk_cancelBlock:(id <NSObject, NSCopying>)block;static id <NSObject, NSCopying> BKDispatchCancellableBlock(dispatch_queue_t queue, NSTimeInterval delay, void(^block)(void)) {
dispatch_time_t time = BKTimeDelay(delay); #if DISPATCH_CANCELLATION_SUPPORTED
if (BKSupportsDispatchCancellation()) {
dispatch_block_t ret = dispatch_block_create(, block);
dispatch_after(time, queue, ret);
return ret;
}
#endif
//cancelled是个__block变量,使得该block在加入queue后能够逻辑上取消。注意,仅仅是逻辑上取消,不能把block从queue中剔除。
__block BOOL cancelled = NO;
//在外部block之上加一层能够逻辑取消的代码,使其变为一个wrapper block
//当调用wrapper(YES)的时候就让__block BOOL cancelled = YES,使得以后每次block主体都被跳过。
void (^wrapper)(BOOL) = ^(BOOL cancel) {
//cancel参数是为了在外部能够控制cancelled _block变量
if (cancel) {
cancelled = YES;
return;
}
if (!cancelled) block();
};
//每个投入queue中的block实际上是wraper版的block
dispatch_after(time, queue, ^{
//把cancel设置为NO,block能够逻辑执行
wrapper(NO);
});
//返回wraper block,以便bk_cancelBlock的时候使用
return wrapper;
}
+ (void)bk_cancelBlock:(id <NSObject, NSCopying>)block
{
NSParameterAssert(block != nil); #if DISPATCH_CANCELLATION_SUPPORTED
if (BKSupportsDispatchCancellation()) {
dispatch_block_cancel((dispatch_block_t)block);
return;
}
#endif
//把cancel设置为YES,修改block中_block cancelled变量,如果此时block未执行则,block在执行的时候其逻辑主体会被跳过
void (^wrapper)(BOOL) = (void(^)(BOOL))block;
wrapper(YES);
}4)、KVO相关BlocksKit
KVO主要涉及两类对象,即“被观察对象“和“观察者“。
与“被观察对象”相关的函数主要有如下两个://添加观察者
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
//删除观察者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context;
//与“观察者“相关的函数如下:
//观察到对象发生变化后的回调函数(观察回调)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;通常的KVO做法是先对“被观察对象”添加“观察者”,同时在“观察者”中实现观察回调。这样每当“被观察对象”的指定property改变时,“观察者”就会调用观察回调。
KVO相关BlocksKit弱化了“观察者”这种对象,使得每当“被观察对象”的指定property改变时,就会调起一个block。具体实现方式是定义一个_BKObserver类,让该类实现观察回调、对被观察对象添加观察者和删除观察者。_BKObserver类定义如下:
@interface _BKObserver : NSObject {
BOOL _isObserving;
} //存储“被观察的对象”
@property (nonatomic, readonly, unsafe_unretained) id observee;
@property (nonatomic, readonly) NSMutableArray *keyPaths;
//存储回调block
@property (nonatomic, readonly) id task;
@property (nonatomic, readonly) BKObserverContext context; - (id)initWithObservee:(id)observee keyPaths:(NSArray *)keyPaths context:(BKObserverContext)context task:(id)task; @endstatic void *BKObserverBlocksKey = &BKObserverBlocksKey;
static void *BKBlockObservationContext = &BKBlockObservationContext; @implementation _BKObserver - (id)initWithObservee:(id)observee keyPaths:(NSArray *)keyPaths context:(BKObserverContext)context task:(id)task
{
if ((self = [super init])) {
_observee = observee;
_keyPaths = [keyPaths mutableCopy];
_context = context;
_task = [task copy];
}
return self;
} - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
//观察者回调,在KV改变的时候调用相关block
if (context != BKBlockObservationContext) return; @synchronized(self) {
switch (self.context) {
case BKObserverContextKey: {
void (^task)(id) = self.task;
task(object);
break;
}
case BKObserverContextKeyWithChange: {
void (^task)(id, NSDictionary *) = self.task;
task(object, change);
break;
}
case BKObserverContextManyKeys: {
void (^task)(id, NSString *) = self.task;
task(object, keyPath);
break;
}
case BKObserverContextManyKeysWithChange: {
void (^task)(id, NSString *, NSDictionary *) = self.task;
task(object, keyPath, change);
break;
}
}
}
} //开启KV观察
- (void)startObservingWithOptions:(NSKeyValueObservingOptions)options
{
@synchronized(self) {
if (_isObserving) return; [self.keyPaths bk_each:^(NSString *keyPath) {
//observee的被观察对象,observer是自己,
[self.observee addObserver:self forKeyPath:keyPath options:options context:BKBlockObservationContext];
}]; _isObserving = YES;
}
} //停止KV观察
- (void)stopObservingKeyPath:(NSString *)keyPath
{
NSParameterAssert(keyPath); @synchronized (self) {
if (!_isObserving) return;
if (![self.keyPaths containsObject:keyPath]) return; NSObject *observee = self.observee;
if (!observee) return; [self.keyPaths removeObject: keyPath];
keyPath = [keyPath copy]; if (!self.keyPaths.count) {
_task = nil;
_observee = nil;
_keyPaths = nil;
} [observee removeObserver:self forKeyPath:keyPath context:BKBlockObservationContext];
}
}
@end使用BlocksKit
- (void)bk_addObserverForKeyPaths:(NSArray *)keyPaths identifier:(NSString *)token options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSString *keyPath, NSDictionary *change))task; - (void)bk_removeObserverForKeyPath:(NSString *)keyPath identifier:(NSString *)token;
(5)、定时器相关BlocksKit
NSTimer有个比较恶心的特性,它会持有它的target。比如在一个controller中使用了timer,并且timer的target设置为该controller本身,那么想在controller的dealloc中fire掉timer是做不到的,必须要在其他的地方fire。这会让编码很难受。具体参考《Effective Objective C》的最后一条。 BlocksKit解除这种恶心,其方式是把timer的target设置为timer 的class对象。把要执行的block保存在timer的userInfo中执行。因为timer 的class对象一直存在,所以是否被持有其实无所谓。
实现代码如下:
//"Replaced with -bk_performAfterDelay:usingBlock:"
+ (id)bk_scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(NSTimer *timer))block repeats:(BOOL)inRepeats
{
NSParameterAssert(block != nil);
return [self scheduledTimerWithTimeInterval:inTimeInterval target:self selector:@selector(bk_executeBlockFromTimer:) userInfo:[block copy] repeats:inRepeats];
} + (id)bk_timerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(NSTimer *timer))block repeats:(BOOL)inRepeats
{
NSParameterAssert(block != nil);
return [self timerWithTimeInterval:inTimeInterval target:self selector:@selector(bk_executeBlockFromTimer:) userInfo:[block copy] repeats:inRepeats];
} + (void)bk_executeBlockFromTimer:(NSTimer *)aTimer {
void (^block)(NSTimer *) = [aTimer userInfo];
if (block) block(aTimer);
}2、动态代理样例
代理是objective c里常用的模式,主要用来做逻辑切分,一个类做一类事情,让代码的耦合度减少。但他不方便的地方在于,要创建一个代理,就要定义一个类,声明这个类遵循那些接口,然后实现这些接口对应的函数。动态代理(Dynamic delegate)则让我们能够在code里,on the fly的创建这样一个代理,通过block定义要实现的方法。
例如:- (void)annoyUser
{
// 创建一个alert view
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:@"Hello World!"
message:@"This alert's delegate is implemented using blocks. That's so cool!"
delegate:nil
cancelButtonTitle:@"Meh."
otherButtonTitles:@"Woo!", nil];
// 获取该alert view的动态代理对象(什么是动态代理对象稍后会说)
A2DynamicDelegate *dd = alertView.bk_dynamicDelegate; // 调用动态代理对象的 - (void)implementMethod:(SEL)selector withBlock:(id)block;方法,使得SEL映射一个block对象(假设叫做block1)
[dd implementMethod:@selector(alertViewShouldEnableFirstOtherButton:) withBlock:^(UIAlertView *alertView) {
NSLog(@"Message: %@", alertView.message);
return YES;
}]; // 同上,让映射-alertView:willDismissWithButtonIndex:的SEL到另外一个block对象(假设叫做block2)
[dd implementMethod:@selector(alertView:willDismissWithButtonIndex:) withBlock:^(UIAlertView *alertView, NSInteger buttonIndex) {
NSLog(@"You pushed button #%d (%@)", buttonIndex, [alertView buttonTitleAtIndex:buttonIndex]);
}];
// 把alertView的delegate设置为动态代理
alertView.delegate = dd;
[alertView show];
}
// 那么,alert view在显示的时候收到alertViewShouldEnableFirstOtherButton:消息调用block1;alert view在消失的时候收到alertView:willDismissWithButtonIndex:消息,调用block2- 1、dd内部可能有个dic一样的数据结构,key可能是SEL,value可能是与之对应的block,通过implementMethod:withBlock:这个方法把SEL和block以键值对的形式建立起dic映射
- 2、Host对象(本例是alertView)向dd发delegate消息的时候传递了SEL,dd在内部的dic数据结构查找对应的block,找到后,调用该block。
3、UIKit相关的Block
拿UIControl打比方,要想处理一个事件:
- 以前:
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
需要通过上述方法将某一个对象的某一个selector传入,一般的做法是在viewcontroller里定义一个方法专门处理某一个按钮的点击事件。
- 现在:
- (void)bk_addEventHandler:(void (^)(id sender))handler forControlEvents:(UIControlEvents)controlEvents;
通过上述方法将一个block注册上去,不需要单独定义方法。
例如:
[btn bk_addEventHandler:^(id _Nonnull sender) {
NSLog(@"");
} forControlEvents:UIControlEventTouchUpInside];
- 现在:
BlocksKit的使用的更多相关文章
- 第2月第25天 BlocksKit
1.blockskit https://github.com/zwaldowski/BlocksKit bk_showAlertViewWithTitle 2.toast +(void)showToa ...
- BlocksKit初见:一个支持将delegate转换成block的Cocoa库
简介 项目主页: https://github.com/zwaldowski/BlocksKit BlocksKit 是一个开源的框架,对 Cocoa 进行了扩展,将许多需要通过 delegate 调 ...
- 神奇的 BlocksKit(1):源码分析(下)
私有类 _BKObserver _BKObserver 是用来观测属性的对象,它在接口中定义了 4 个属性: @property (nonatomic,readonly,unsafe_unretain ...
- 神奇的 BlocksKit(1):源码分析(上)
高能预警:本篇文章非常长,因为 BlocksKit 的实现还是比较复杂和有意的.这篇文章不是为了剖析 iOS 开发中的 block 的实现以及它是如何组成甚至使用的,如果你想通过这篇文章来了解 blo ...
- BlocksKit(2)-DynamicDelegate
BlocksKit(2)-DynamicDelegate 动态代理可以说是这个Block里面最精彩的一部分了,可以通过自己给一个类的的协议方法指定对应的block来实现让这个协议的回调都直接在bloc ...
- BlocksKit(1)-基本类型的分类
BlocksKit(1)-基本类型的分类 BlocksKit是一个用block的方式来解决我们日常用对集合对象遍历.对象快速生成使用.block作为委托对象的一种综合便利封装库.这个库主要分三个大块C ...
- BlocksKit block从配角到主角—oc通往函数式编程之路--oc rx化?
BlocksKit 对 oc语言的功能层.UI层进行了大量的适配,使得oc能够复合函数式编程的风格: 是oc语言的函数式编程风格改造: 将函数式风格的BlocksKit API解释为原生的功能调用: ...
- 开源 iOS 项目分类索引大全 - 待整理
开源 iOS 项目分类索引大全 GitHub 上大概600个开源 iOS 项目的分类和介绍,对于你挑选和使用开源项目应该有帮助 系统基础库 Category/Util sstoolkit 一套Cate ...
- 用block做事件回调来简化代码,提高开发效率
我们在自定义view的时候,通常要考虑view的封装复用,所以如何把view的事件回调给Controller就是个需要好好考虑的问题, 一般来说,可选的方式主要有target-action和de ...
随机推荐
- java中 hashCode() 和 equals()
1. 值类型是存储在内存中的栈,而引用类型的变量在栈中仅仅是存储引用类型变量的地址来自堆,而其本身则存储在栈中. 2. ==操作比较的是两个变量的值是否相等, 3. 对于引用型变量表示的是两个变量在堆 ...
- poj 1163 The Triangle &poj 3176 Cow Bowling (dp)
id=1163">链接:poj 1163 题意:输入一个n层的三角形.第i层有i个数,求从第1层到第n层的全部路线中.权值之和最大的路线. 规定:第i层的某个数仅仅能连线走到第i+1层 ...
- Xcode5.1.1+ios 7.1.2 免证书真机调试
Xcode假设不破解.无法真机调试, 也无法编译真机Release文件.仅仅有付费开通Apple开发人员账号,才干申请真机调试.而Xcode进行破解后,结合越狱的iPhone或iPad, 就可以免官方 ...
- wpf中的样式与模板
1.WPF样式类似于Web应用程序中的CSS,在WPF中可以为控件定义统一的样式(Style).样式属于资源的一种,例如为Button定义统一的背景颜色和字体: <Window.Resource ...
- Java中的枚举类为何不能有public构造器
声明:本博客为原创博客.未经同意.不得转载!原文链接为http://blog.csdn.net/bettarwang/article/details/27262809. 从Java 5開始有了枚举类, ...
- 【BZOJ3307】雨天的尾巴 线段树合并
[BZOJ3307]雨天的尾巴 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多 ...
- src/github.com/mongodb/mongo-go-driver/mongo/cursor.go 游标的简洁实用
src/github.com/mongodb/mongo-go-driver/mongo/cursor.go // Copyright (C) MongoDB, Inc. 2017-present./ ...
- java random配置修改
不知道 报啥错的时候 ,改这个 vim /usr/java/latest/jre/lib/security/java.security 原值: securerandom.source=file:/de ...
- OSI和TCP/IP
OSI和TCP/IP 1. OSI的七层网络结构(功能及特点) 1) 物理层:为数据链路层提供物理连接,在其上串行传送比特流,即所传送数据的单位是比特.此外,该层中还具有确定连接设备的 ...
- ES6 对Math对象的扩展
Math 对象的扩展 Math.trunc() Math.trunc(4.1) // 4 Math.trunc(4.9) // 4 Math.trunc(-4.1) // -4 Math.trunc( ...