闲言稍絮
什么是动态功能部署、动态功能部署有什么优缺点、市面上都有哪些动态功能部署的方案、这些方案横向对比优点缺点各是什么等等,类似的问题及答案就不赘述了,某度一搜相关的文章有好几十页,有兴趣的可以去看看,下面直接讨论我们的实现方式。
NJCS的整体介绍
整体设计

阅读全文 »

[xxx.navigationController pushViewController:yyy animated:YES/NO];
[xxx.navigationController popViewControllerAnimated:YES?NO];
以及
[xxx presentViewController:yyy animated:YES/NO completion:nil];
[xxx dismissViewControllerAnimated:YES/NO completion:nil];
以上几个关于视图转换的方法我们在开发过程中经常用到,但是在这些方法在执行完成之后各个viewController之间是以什么样的关系存在的,没有具体的想过,之前也看过一篇关于相关内容的文章,时间久了再想看时文章已经找不到了,所以这次打算写代码测一下结果,并总结记录下来:
UIViewController分为两种类型:
viewController容器,如:UITabBarController、UINavigationController等
viewcontroller:UIViewController及其自定义的子类

阅读全文 »

https请求过程

HTTPS其实是有两部分组成:HTTP + SSL / TLS,
也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会通过TLS进行加密,所以传输的数据都是加密后的数据

阅读全文 »

NJCS在接入的时候需要传入js所在的文件路径,初始化时会去相应的路径读取文件,如果文件不存在则会去NSBundle内读取内置文件,所以如果想更新JS文件只需要将最新的文件放入接入时指定的路径即可,目前我们的做法是:
1:app在启动后会拉取一个版本配置文件,里面包含了三个字段
version:js文件版本号
url:js文件地址
controllers:需要被关闭的vc名字如:[“MineViewController”, “setViewController”]
读取配置文件版本号和本地的js版本做对比,如果不相同则从url处拉取最新js zip(如:3.zip)包,并解压到指定文件夹下,解压缩成功后更新本地version字段
2:下次启动时读取最新版本的js文件,并将旧版本文件删除

目前内置的扩展实现有:
NJCSVCManager
NJCSViewAnimate
NJCSNotificationManager
NJCSUserDefaultManager
NJCSModelAdapter
其余的扩展都是接口层的实现,需要用户根据自己的情况具体实现,如:
NJCSHttp
NJCSAlert
NJCSLogComponent
等……

1:下面以NJCSHttp为例进行扩展接入介绍:
接口协议:

#import 
#import 

typedef void(^callbackBlock)(BOOL isSuccess, id resultData);

@protocol NJCSHttpInterfaceProtocol 
//
//  外部需要实现的http get请求
//
//  @param urlString   接口url 字符串
//  @param params      请求参数字典
//  @param resultBlock 请求结束后的block
//
- (void)Get:(NSString *)urlString params:(NSDictionary *)params resultBlock:(void(^)(BOOL isSuccess, id resultData))resultBlock;
//
//  外部需要实现的http post请求
//
//  @param urlString   接口url 字符串
//  @param params      请求参数字典
//  @param resultBlock 请求结束后的block
//
- (void)Post:(NSString *)urlString params:(NSDictionary *)params resultBlock:(void(^)(BOOL isSuccess, id resultData))resultBlock;

@end

@protocol NJCSHttpProtocol 

- (void)GetRequestWithURLString:(NSString *)urlString params:(NSDictionary *)params callbackConfig:(NSDictionary *)callbackConfig;
- (void)PostRequestWithURLString:(NSString *)urlString params:(NSDictionary *)params callbackConfig:(NSDictionary *)callbackConfig;

@end
阅读全文 »

Notification也是通过扩展的方式接进去的,目前支持的功能有:

  • JS->JS
  • JS->Native
  • Native->JS

扩展的方法如下:

- (void)postNotificationName:(NSString *)notificationName object:(id)notificationSender userInfo:(NSDictionary *)userInfo;
- (void)addNotificationName:(NSString *)notificationName processor:(NSDictionary *)processor object:(id)notificationSender;
- (void)removeNotificationName:(NSString *)notificationName instanceName:(NSString *)instanceName;

分别为:
发起Notification
添加Notification
移除Notification

详细实现代码如下:

- (void)postNotificationName:(NSString *)notificationName object:(id)notificationSender userInfo:(NSDictionary *)userInfo {

    [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:notificationSender userInfo:userInfo];
}

- (void)addNotificationName:(NSString *)notificationName processor:(NSDictionary *)processor object:(id)notificationSender {

    if (notificationName && [processor isKindOfClass:[NSDictionary class]]) {

        if (!_processors[notificationName]) _processors[notificationName] = [[NSMutableArray alloc] initWithCapacity:0];

        (!processor) ?: [_processors[notificationName] addObject:processor];

    }

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(p_handleNotification:) name:notificationName object:notificationSender];
}

- (void)removeNotificationName:(NSString *)notificationName instanceName:(NSString *)instanceName {

    if (!notificationName || !instanceName) {

        return;
    }

    [_processors[notificationName] enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([instanceName isEqualToString:obj[kInstanceName]]) {

            [_processors removeObjectForKey:obj];
        }
    }];
}

- (void)p_handleNotification:(NSNotification *)notify {

    [_processors[notify.name] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        [[NJCSBridge getInstance] runJSFunctionWithConfigures:obj];

    }];
}

使用示例:

var notifiConfig = {kInstanceName:"VCInstanceName", kMethodName:"methodName"};//生成配置
nativeBridge.addNotificationName_processor("NOTIFICATIONNAME", notifiConfig, null);//添加监听
nativeBridge.postNotificationName("NOTIFICATIONNAME", null, null);//发起通知

目前动画只支持UIView的几个简单的animate,是通过扩展的方式接入的,直接上代码:
1:接入代码

- (instancetype)init
{
    if (self = [super init]) {
        _viewControllers = [NSMapTable strongToWeakObjectsMapTable];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(njcsViewControllerDealloc:) name:NJCSVIEWCONTROLLER_DEALLOC object:nil];
        _njcsJSContext = [[NJCS_JSContext alloc] init];
        _njcsJSContext.jscontext[@"NJCSBridge_Native"] = self;

        self.macors = [[NJCSMacors alloc] init];
        self.njcsHttp = [[NJCSHttp alloc] init];
        self.viewAnimate = [[NJCSViewAnimate alloc] init];
        self.modelAdapter = [[NJCSModelAdapter alloc] init];
        self.notifyManager = [NJCSNotificationManager sharedManager];
        self.njcsAlert = [NJCSAlert shareInstance];
    }

    return self;
}

viewAnimate是做为必接扩展添加进去的,是用户必须实现的。
对外公开的几个方法如下:

- (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^ _Nullable)(void))animations
{
    [UIView animateWithDuration:duration animations:animations];
}

- (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^ _Nullable)(void))animations completion:(void (^ _Nullable)(BOOL))completion
{
    [UIView animateWithDuration:duration animations:animations completion:completion];
}

- (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^ _Nullable)(void))animations completion:(void (^ _Nullable)(BOOL))completion
{
    [UIView animateWithDuration:duration delay:delay options:options animations:animations completion:completion];
}

都是些很简单的实现,这里就不详细介绍了。

闲话少说,直接上代码:

- (void)dispatch_async_main:(JSValue *)function
{
    NSParameterAssert(function);
    dispatch_async(dispatch_get_main_queue(), ^{
        [function callWithArguments:nil];
    });
}

异步主线程,只是将dispatch_async进行了简单的包装;

- (void)dispatch_async_global_queue:(JSValue *)function afterFunction:(JSValue *)afterFunction
{
    NSParameterAssert(function);
    NSParameterAssert(afterFunction);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        JSValue *result = [function callWithArguments:nil];
        [afterFunction callWithArguments:@[result]];
    });
}

异步全局队列,也是简单的包装,之前看过一片关于JS锁的文章,里面说不管是在哪个线程调用js代码,只要JSContext相同就都会在主线程调用,如果想实现真正的异步,需要新建js虚拟机,后续会进行测试验证;

- (void)dispatch_after:(NSInteger)time function:(JSValue *)function
{
    NSParameterAssert(time);
    NSParameterAssert(function);

    dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC));
    dispatch_after(delayTime, dispatch_get_main_queue(), ^{
        [function callWithArguments:nil];
    });
}

同上,这里都不赘述了,关于其他GCD相关的需要的时候进行封装即可;

一个对象在JS和OC之间传递的时候会有个对应的类型转换,在JSValue.h源码中有个简单的介绍:

@textblock
   Objective-C type  |   JavaScript type
 --------------------+---------------------
         nil         |     undefined
        NSNull       |        null
       NSString      |       string
       NSNumber      |   number, boolean
     NSDictionary    |   Object object
       NSArray       |    Array object
        NSDate       |     Date object
       NSBlock (1)   |   Function object (1)
          id (2)     |   Wrapper object (2)
        Class (3)    | Constructor object (3)
@/textblock
这里重点提一下OC 的 id类型到JS环境后会是Wrapper object,在IOS7 及以下的系统中这个Wrapper object在被访问的时候会引起崩溃异常,这个坑很深,困扰了好几天才想到比较好的解决办法,将id类型封装到NSDictionary内部返回的,详细的情况在[原理部分]()会进行介绍。Native的方法在被执行的时候参数来源有两种,第一种:从OC传给JS,再从JS回传给OC,这种参数是不需要进行类型转换可以直接使用的;第二种:直接在JS生成,简单的类型如:Array、Dictionary、Data会在传递过程中自动转换,复杂的类型:如 CGRect、UIColor以及自定义的数据类型是无法自动转换的就需要手动封装。
阅读全文 »

NJCS内方法的调用有两种方式,这里分别介绍一下:
1:链式调用
代码示例:

var UITableView = getNativeClassWithClassName("UITableView");//根据字符串获取UITableView类
var tableView = UITableView.alloc().init();//初始化UITableView实例
this.view().addSubView(tableView);//获取当前viewController的view 并添加tableView
tableView.setFrame(NJCSCGRect.create(0, 0, NJCSMacors["mainScreenWidth"], NJCSMacors["screenHeight"] - 50));//设置tableView的frame
tableView.setBackgroundColor(NJCSUIColor_JS.create("#999999:0"));//设置tableView的背景颜色
UITableView在执行alloc方法后会返回一个对象,然后在执行该对象的init方法,具体的实现会在[原理文章]()中介绍。 2:簇调用
阅读全文 »