【疯狂造轮子-iOS】JSON转Model系列之二

news2024/11/15 4:39:20

1. 前言


上一篇《【疯狂造轮子-iOS】JSON转Model系列之一》实现了一个简陋的JSON转Model的库,不过还存在很多问题。下面我会尝试一个个去解决。

2. 存在问题及解决思路


2.1 没有考虑JSON数据并不一定是NSDictionary类型

有时候JSON并不一定是NSDictionary类型,可能是一个字符串,也可能是NSData类型的数据。不过不管是哪种类型,统统先将其转化为NSData数据,然后使用+[NSJSONSerialization JSONObjectWithData:options:error:]来转化。所以我在initWithAttributes:上面又封装了一层。

- (instancetype)initWithJSONData:(id)json
{
    NSDictionary *dict = [self pjx_dictionaryWithJSON:json];
    return [self initWithAttributes:dict];
}
 
/**
 * @brief 将NSString和NSData格式的json数据转化为NSDictionary类型
 */
- (NSDictionary *)pjx_dictionaryWithJSON:(id)json
{
    if (!json) {
        return nil;
    }
    // 若是NSDictionary类型,直接返回
    if ([json isKindOfClass:[NSDictionary class]]) {
        return json;
    }
 
    NSDictionary *dict = nil;
    NSData *jsonData = nil;
 
    if ([json isKindOfClass:[NSString class]]) {
        // 如果是NSString,就先转化为NSData
        jsonData = [(NSString*)json dataUsingEncoding:NSUTF8StringEncoding];
    } else if ([json isKindOfClass:[NSData class]]) {
        jsonData = json;
    }
 
    if (jsonData && [jsonData isKindOfClass:[NSData class]]) {
        // 如果时NSData类型,使用NSJSONSerialization
        NSError *error = nil;
        dict = [NSJSONSerialization JSONObjectWithData:jsonData options: error:&error];
        if (error) {
            NSLog(@"pjx_dictionaryWithJSON error:%@", error);
            return nil;
        }
        if (![dict isKindOfClass:[NSDictionary class]]) {
            return nil;
        }
    }
 
    return dict;
}

为此,我在ViewController添加了两个sample。分别用来解析NSString类型的JSON数据和NSData类型的JSON数据。

// NSString类型的JSON数据
- (void)runSimpleSample2
{
    NSString *userStr = @"                                                              \
                        {                                                               \
                            \"username\"       : \"shuaige\",                           \
                            \"password\"       : \"123456\",                            \
                            \"avatarImageURL\" : \"http://www.example.com/shuaige.png\" \
                        }";
 
    PJXUser *user = [[PJXUser alloc] initWithJSONData:userStr];
 
    NSLog(@"runSimpleSample2\n");
    NSLog(@"----------------------------------------");
    NSLog(@"username:%@\n",user.username);
    NSLog(@"password:%@\n",user.password);
    NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
}
 
// NSData类型的JSON数据
- (void)runSimpleSample3
{
    NSString *userInfoFilePath = [[NSBundle mainBundle] pathForResource:@"UserInfo" ofType:@"txt"];
    NSData *data = [NSData dataWithContentsOfFile:userInfoFilePath];
    PJXUser *user = [[PJXUser alloc] initWithJSONData:data];
 
    NSLog(@"runSimpleSample3\n");
    NSLog(@"----------------------------------------");
    NSLog(@"username:%@\n",user.username);
    NSLog(@"password:%@\n",user.password);
    NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
}

输出结果也是正确的:

2.2 没有考虑用户传入的JSON数据的key值和property的名称不一致

我第一反应是使用一个映射表。也就是说用户使用时需要自定义一套property和key的映射表。YYModel中使用了一个+ (NSDictionary *)modelCustomPropertyMapper函数,用户可以自定义该函数达到映射表的效果,而这个函数是放在一个protocol中的。我挺认同这种设计的,因为modelCustomPropertyMapper这种函数和Model是一种组合关系,可有可无(optional),所以设计成协议更合适。但是作者在设计protocol又说了一句:

// There's no need to add '<YYModel>' to your class header.
@protocol YYModel <NSObject>

 

什么意思呢,就是说你自定义一个NSObject子类(如YYBook)时,如果想实现自定义的property映射关系,只需要实现modelCustomPropertyMapper函数即可,而不需要写成@interface YYBook : NSObject <YYModel>。作者的意思是你遵不遵循YYModel这个protocol都没事,反正你只要在YYBook实现了modelCustomPropertyMapper即可。具体解释,大家请参考这个issue。

这种设计我不是很赞同,我是有洁癖的人,要不然你就别定义YYModel这个protocol,说明文档里面着重说明一下就行。所以此处我还是选择判断NSObject的子类是否遵循protocol,也就是说只有遵循了这个protocol,才能自定义property映射关系。

首先我们看如何使用自定义propertyMapper。我先建立一个PJXUserPropertyMapper类,遵循了JSONProtocol协议,并实现了propertyMapper协议函数。

// 遵循JSONProtocol协议,这个JSONProtocol中定义的就是我的propertyMapper协议函数
@interface PJXUserPropertyMapper : NSObject <JSONProtocol>
 
@property (nonatomic, copy) NSString* username; // 用户名
@property (nonatomic, copy) NSString* password; // 密码
@property (nonatomic, copy) NSString* avatarImageURL; // 头像的URL地址
 
@end
 
@implementation PJXUserPropertyMapper
// 实现propertyMapper这个协议方法
+ (NSDictionary *)propertyMapper
{
    return @{@"Username" : @"username",
             @"Password" : @"password",
             @"AvatarImageURL" : @"avatarImageURL"};
}
 
@end

随后我定义了一个example。

#pragma mark - PropertyMapper Sample
- (void)runPropertyMapperSample
{
    NSDictionary *userDict = @{@"Username" : @"shuaige",
                               @"Password" : @"",
                               @"AvatarImageURL" : @"http://www.example.com/shuaige.png"};
    PJXUserPropertyMapper *user = [[PJXUserPropertyMapper alloc] initWithJSONData:userDict];
 
    NSLog(@"runPropertyMapperSample\n");
    NSLog(@"----------------------------------------");
    NSLog(@"username:%@\n",user.username);
    NSLog(@"password:%@\n",user.password);
    NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
}

是不是感觉调用上和之前的非property映射没什么区别?那是因为我们需要在initWithJSONData中增加一些东西。

具体的做法是在PropertyWithDictionary函数增加了一个查表操作。

// 注意我传入的dictionary就是用户提供的JSON数据
// 比如此处传入的key==@"username",value==@"shuaige"
static void PropertyWithDictionaryFunction(const void *key, const void *value, void *context)
{
    NSString *keyStr    = (__bridge NSString *)(key);
 
    ......       
 
    // 如果使用了JSONProtocol,并且自定义了propertyMapper,那么还需要将keyStr转化下
    if ([modelSelf conformsToProtocol:@protocol(JSONProtocol)] && [[modelSelf class] respondsToSelector:@selector(propertyMapper)]) {
        keyStr = [[[modelSelf class] propertyMapper] objectForKey:keyStr];
    }
 
    ......
}

这样就可以啦.我们看看效果:

2.3 没有考虑JSON数据的value值不一定是NSString类型

开始的时候,挺担心我这种写法会不会不兼容别的数据类型。不过我觉得应该没什么问题,毕竟我使用的setter方法本质上没啥问题,我的类型全用id来代替了(事实上,我的想法大错特错):

((void (*)(id, SEL, id))(void *) objc_msgSend)(modelSelf, info.setter, setValue);

 不过本着不怕一万,就怕万一的心态。我还是做了一个example来试验一下:

@interface PJXUserVariousType : NSObject
 
@property (nonatomic, copy) NSString *blogTitle; // 博客标题
@property (nonatomic, strong) NSURL *blogURL; // 博客网址
@property (nonatomic, assign) NSInteger blogIndex; // 博客索引值
@property (nonatomic, strong) NSDate *postDate; // 博客发布时间
@property (nonatomic, strong) NSArray *friends; // 我的好友名称
@property (nonatomic, strong) NSSet *collections; // 我的收藏
 
@end
 
@implementation PJXUserVariousType
 
@end
 
#pragma mark - VariousType Sample
- (void)runVariousTypeSample
{
    NSDictionary *userDict = @{@"blogTitle" : @"iOS developer",
                               @"blogURL" : @"http://www.example.com/blog.html",
                               @"blogIndex" : @,
                               @"postDate" : [NSDate date],
                               @"friends" : @[@"meinv1", @"meinv2", @"meinv3"],
                               @"collections" : @[@"shuaige1", @"shuaige2", @"shuaige3"]};
    PJXUserVariousType *user = [[PJXUserVariousType alloc] initWithJSONData:userDict];
 
    NSLog(@"runVariousTypeSample\n");
    NSLog(@"----------------------------------------");
    NSLog(@"blogTitle:%@\n",user.blogTitle);
    NSLog(@"blogURL:%@\n",user.blogURL);
    NSLog(@"blogIndex:%ld\n",user.blogIndex);
    NSLog(@"postDate:%@\n",user.postDate);
    NSLog(@"friends:%@\n",user.friends);
    NSLog(@"collections:%@\n",user.collections);
}

你猜输出啥?

其他都正确,唯独我们的blogIndex出错了。这里确实是我欠考虑了,类似NSInteger,BOOL这些NSNumber类型(我暂时只考虑这些常用类型)需要单独处理一下。这一部分看起来容易,但是为了处理这种特殊情况确实要下很大功夫。比如你得先判断该属性是不是double或int这种类型,只有判断除了该属性是double还是int,你才能正确使用setter方法,而此处的调用方式也要单独写一个,因为和之前调用方式有一些些区别,需要判断Number的类型是double,是int,还是BOOl…….

对此我在PJXPropertyInfo中定义了两个函数,一个叫isNumber,用来判断该属性是不是一个Number,另一个叫setNumberValue:withModelSelf:,用来给是Number类型的属性赋值。另外,我仿照YYModel(比YYModel简化很多了)建了一个PJXEncodingType的enum类型,用来存储Number的类型(int?double?BOOL?……),与之配套的还有一个PJXGetEncodingType函数,来获取当前属性的类型(是int?double?BOOL?),具体怎么做还挺复杂的,后面会详细说明。

代码如下:

// Number类型
typedef NS_ENUM(NSUInteger, PJXEncodingType) {
    PJXEncodingTypeUnknown    = , ///< unknown
    PJXEncodingTypeBool       = 1, ///< bool
    PJXEncodingTypeInt8       = 2, ///< char / BOOL
    PJXEncodingTypeUInt8      = 3, ///< unsigned char
    PJXEncodingTypeInt16      = 4, ///< short
    PJXEncodingTypeUInt16     = 5, ///< unsigned short
    PJXEncodingTypeInt32      = 6, ///< int
    PJXEncodingTypeUInt32     = 7, ///< unsigned int
    PJXEncodingTypeInt64      = 8, ///< long long
    PJXEncodingTypeUInt64     = 9, ///< unsigned long long
    PJXEncodingTypeFloat      = 10, ///< float
    PJXEncodingTypeDouble     = 11, ///< double
    PJXEncodingTypeLongDouble = 12, ///< long double
};
 
// 根据objc_property_attribute_t可以获取到property的类型PJXEncodingType
// 参考YYModel
PJXGetEncodingType(const char *encodingType) {
    char *type = (char *)encodingType;
    if (!type) return PJXEncodingTypeUnknown;
    size_t len = strlen(type);
    if (len == ) return PJXEncodingTypeUnknown;
 
    switch (*type) {
        case 'B': return PJXEncodingTypeBool;
        case 'c': return PJXEncodingTypeInt8;
        case 'C': return PJXEncodingTypeUInt8;
        case 's': return PJXEncodingTypeInt16;
        case 'S': return PJXEncodingTypeUInt16;
        case 'i': return PJXEncodingTypeInt32;
        case 'I': return PJXEncodingTypeUInt32;
        case 'l': return PJXEncodingTypeInt32;
        case 'L': return PJXEncodingTypeUInt32;
        case 'q': return PJXEncodingTypeInt64;
        case 'Q': return PJXEncodingTypeUInt64;
        case 'f': return PJXEncodingTypeFloat;
        case 'd': return PJXEncodingTypeDouble;
        case 'D': return PJXEncodingTypeLongDouble;
 
        default: return PJXEncodingTypeUnknown;
    }
}
 
/**
 * @brief 存储Model中每个property的信息
 * ......
 * @param type 是一个PJXEncodingType类型变量,为了存储该属性是哪种Number(int?double?BOOL?)
 */
@interface PJXPropertyInfo : NSObject
......
@property (nonatomic, assign) PJXEncodingType type;
@end
 
@implementation PJXPropertyInfo
 
- (instancetype)initWithPropertyInfo:(objc_property_t)property
{
    self = [self init];
 
    if (self) {
        ......
 
        // 判断属性类型
        unsigned int attrCount;
        // 关于objc_property_attribute_t,这里有一篇文章介绍的很好
        // http://www.henishuo.com/runtime-property-ivar/
        objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
        for (unsigned int i = ; i < attrCount; i++) {
            switch (attrs[i].name[]) {
                case 'T': {//  EncodingType
                    if (attrs[i].value) {
                        //NSLog(@"attrs[%d].value = %s", i, attrs[i].value);
                        // 可以根据value获取到property类型
                        _type = PJXGetEncodingType(attrs[i].value);
                    }
                    break;
                }
                default:
                    break;
            }
        }
        ......
    }
 
    return self;
}
 
// 根据propertyInfo中存储的type判断其是否为Number
- (BOOL)isNumber
{
    switch (self.type) {
        case PJXEncodingTypeBool:
        case PJXEncodingTypeInt8:
        case PJXEncodingTypeUInt8:
        case PJXEncodingTypeInt16:
        case PJXEncodingTypeUInt16:
        case PJXEncodingTypeInt32:
        case PJXEncodingTypeUInt32:
        case PJXEncodingTypeInt64:
        case PJXEncodingTypeUInt64:
        case PJXEncodingTypeFloat:
        case PJXEncodingTypeDouble:
        case PJXEncodingTypeLongDouble:
            return YES;
        default:
            return NO;
            break;
    }
}
 
// 使用objc_msgSend调用modelSelf中该属性对应的setter方法
- (void)setNumberValue:(NSNumber *)number withModelSelf:(id)modelSelf
{
    switch (self.type) {
        case PJXEncodingTypeBool:
            ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.boolValue);
            break;
        case PJXEncodingTypeInt8:
            ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.charValue);
            break;
        case PJXEncodingTypeUInt8:
            ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedCharValue);
            break;
        case PJXEncodingTypeInt16:
            ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.shortValue);
            break;
        case PJXEncodingTypeUInt16:
            ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedShortValue);
            break;
        case PJXEncodingTypeInt32:
            ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.intValue);
            break;
        case PJXEncodingTypeUInt32:
            ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedIntValue);
            break;
        case PJXEncodingTypeInt64:
            ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)(modelSelf, self.setter, number.longLongValue);
            break;
        case PJXEncodingTypeUInt64:
            ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedLongLongValue);
            break;
        case PJXEncodingTypeFloat:
            ((void (*)(id, SEL, float))(void *) objc_msgSend)(modelSelf, self.setter, number.floatValue);
            break;
        case PJXEncodingTypeDouble:
            ((void (*)(id, SEL, double))(void *) objc_msgSend)(modelSelf, self.setter, number.doubleValue);
            break;
        case PJXEncodingTypeLongDouble:
            ((void (*)(id, SEL, long double))(void *) objc_msgSend)(modelSelf, self.setter, number.doubleValue);
            break;
        default:
            break;
    }
}
 
@end

 有了上述的几个方法,后面就好办了,只需在PropertyWithDictionaryFunction函数中添加一个Number的判断就行:

static void PropertyWithDictionaryFunction(const void *key, const void *value, void *context)
{
    ......
 
    // 如果该属性是Number,那么就用Number赋值方法给其赋值
    if ([info isNumber]) {
        [info setNumberValue:setValue withModelSelf:modelSelf];
    } else {
        ((void (*)(id, SEL, id))(void *) objc_msgSend)(modelSelf, info.setter, setValue);
    }
}

这下终于成功了:

2.4 没有考虑用户自定义了Model属性的setter方法

这个其实比较简单,只需要对property的attribute(objc_property_attribute_t)进行判断即可:

- (instancetype)initWithPropertyInfo:(objc_property_t)property
{
        ......
 
        BOOL isCustomSetter = NO;
        // 判断属性类型
        unsigned int attrCount;
        // 关于objc_property_attribute_t,这里有一篇文章介绍的很好
        // http://www.henishuo.com/runtime-property-ivar/
        objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
        for (unsigned int i = ; i < attrCount; i++) {
            switch (attrs[i].name[]) {
                case 'T': { // EncodingType
                    if (attrs[i].value) {
                        //NSLog(@"attrs[%d].value = %s", i, attrs[i].value);
                        // 可以根据value获取到property类型
                        _type = PJXGetEncodingType(attrs[i].value);
                    }
                    break;
                }
                case 'S': { // 自定义setter方法
                    if (attrs[i].value) {
                        isCustomSetter = YES;
                        _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
                    }
                } break;
                default:
                    break;
            }
        }
 
        if (!isCustomSetter) {
            // 如果没有自定义setter方法,只考虑系统默认生成setter方法
            // 也就是说属性username的setter方法为setUsername:
            NSString *setter = [NSString stringWithFormat:@"%@%@", [_name substringToIndex:].uppercaseString, [_name substringFromIndex:]];
            _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@:", setter]);
        }
    }
 
    return self;
}

 使用下面这个例子测试:

@interface PJXUserCustomSetter : NSObject
 
@property (nonatomic, copy, setter=setCustomUserName:) NSString* username; // 用户名
@property (nonatomic, copy, setter=setCustomBirthday:) NSDate* birthday; // 生日
 
@end
 
@implementation PJXUserCustomSetter
 
- (void)setCustomUserName:(NSString *)username
{
    _username = [NSString stringWithFormat:@"My name is %@", username];
}
 
- (void)setCustomBirthday:(NSDate *)birthday
{
    NSTimeInterval timeInterval = **; // 过一天
    _birthday = [NSDate dateWithTimeInterval:timeInterval sinceDate:birthday];
}
 
@end
 
#pragma mark - Custom Setter Sample
- (void)runCustomSetterSample
{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate *birthday = [dateFormatter dateFromString:@"2016-04-07 00:20:03"];
    NSDictionary *userDict = @{@"username" : @"shuaige",
                               @"birthday" : birthday};
    PJXUserCustomSetter *user = [[PJXUserCustomSetter alloc] initWithJSONData:userDict];
 
    NSLog(@"runCustomSetterSample\n");
    NSLog(@"----------------------------------------");
    NSLog(@"username:%@\n",user.username);
    NSLog(@"birthday:%@\n",user.birthday);
}

得到的结果为:


成功了.

2.5 没有考虑用户传入的JSON数据有嵌套

我个人感觉这个应该没什么问题,为什么这么说呢?因为我嵌套的无非也是一个NSObject类型,那么就调用其自身的setter方法就OK啊.不过还是以防万一,我构造了一下案例:

@interface PJXBlog : NSObject
 
@property (nonatomic, copy) NSString *title; // 博客名称
@property (nonatomic, strong) NSDate *postDate; // 博客发表日期
@property (nonatomic, copy) PJXUser *author; // 博客作者
 
@end
 
@implementation PJXBlog
 
@end
 
#pragma mark - Nest Sample
- (void)runNestSample
{
    NSDictionary *blogDict = @{@"title" : @"how to convert JSON to Model?",
                               @"postDate" : [NSDate date],
                               @"author" : @{@"username" : @"shuaige",
                                             @"password" : @"",
                                             @"avatarImageURL":@"http://www.example.com/shuaige.png"}};
    PJXBlog *blog = [[PJXBlog alloc] initWithJSONData:blogDict];
 
    NSLog(@"runNestSample\n");
    NSLog(@"----------------------------------------");
    NSLog(@"title:%@\n",blog.title);
    NSLog(@"postDate:%@\n",blog.postDate);
    NSLog(@"author:%@\n",blog.author);
}

 

输出结果如下:

结果没什么问题.不过这样说可能不是很负责任,但是目前我也想不到反例.暂时先当做成功了.

3. 总结


以我的能力,目前只能将JSON转化Model实现到这个地步了.总体来说,实现的难度不是很大(因为我考虑的情况还是比较少的,另外还有些功能没添加),不过涉及的知识点还是挺多的,挺不错的一个练手项目:).

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/498819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【医学影像数据处理】2D/3D patch的crop和merge操作汇总

在做3D分割任务中&#xff0c;多数的方法多采用整体缩放&#xff0c;或裁剪成一个个小的patch操作&#xff0c;这样做的一个主要原因是内存问题。 相较于整体缩放&#xff0c;采用裁剪成patch的方法&#xff0c;对于小目标会更加的鲁棒&#xff0c;这也是大多数3D分割任务中常…

Leetcode448. 找到所有数组中消失的数字

Every day a leetcode 题目来源&#xff1a;448. 找到所有数组中消失的数字 解法1&#xff1a;STL set set 是一个集合类型的容器&#xff0c;里面的元素具有唯一性&#xff0c;并且所有元素都会根据元素的键值自动被排序&#xff0c;以红黑树为底层数据结构。 我们使用集合…

git上传大大大文件项目好折磨人

本来想把unity项目的源码上传上gitee啊&#xff0c;但是那个项目有1个多G&#xff0c;还是个半成品&#xff0c;要是写完&#xff0c;都不知道行不行 正常的上传 所用到的命令&#xff1a; 1、 git init 初始化&#xff0c;创建本地仓库 2、 git add . 添加到本地仓库 3、 git…

【C++】打开C++大门,踏入C++世界

文章目录 ☑️前言一. 浅看【C】关键字二. 命名空间1. 命名空间的定义&#x1f44c;2. 命名空间的使用&#x1f44c; 三. 【C】输入输出(IO)四. 缺省参数1. 缺省参数的概念&#x1f3c6;2. 缺省参数的分类&#x1f3c6; 五. 函数重载1. 函数重载的概念✌️2. 【C】支持函数重载…

【计算机专业漫谈】【计算机系统基础学习笔记】W2-2-2 模运算系统和补码表示

利用空档期时间学习一下计算机系统基础&#xff0c;以前对这些知识只停留在应试层面&#xff0c;今天终于能详细理解一下了。参考课程为南京大学袁春风老师的计算机系统基础MOOC&#xff0c;参考书籍也是袁老师的教材&#xff0c;这是我的听课自查资料整理后的笔记 补码表示法…

实验9 分类问题

1. 实验目的 ①掌握逻辑回归的基本原理&#xff0c;实现分类器&#xff0c;完成多分类任务&#xff1b; ②掌握逻辑回归中的平方损失函数、交叉熵损失函数以及平均交叉熵损失函数。 2. 实验内容 ①能够使用TensorFlow计算Sigmoid函数、准确率、交叉熵损失函数等&#xff0c;…

GEE:基于变异系数法的遥感环境风险评估指数(RSEI)计算

作者:CSDN @ _养乐多_ 本文记录了基于变异系数法计算 Risk-Screening Environmental Indicators (RSEI) 的方法和代码。使用 变异系数法计算权重来代替PCA方法计算权重,根据权重计算综合指标。本文也记录了使用landsat-8数据计算LST、NDVI、NDBSI、WET四个指标的代码。 结果…

密码学:其他常见密码学应用.

密码学&#xff1a;其他常见密码学应用. 密码学是研究编制密码和破译密码的技术科学。研究密码变化的客观规律&#xff0c;应用于编制密码以保守通信秘密的&#xff0c;称为编码学&#xff1b;应用于破译密码以获取通信情报的&#xff0c;称为破译学&#xff0c;总称密码学. 目…

二叉树基础概念详解

文章目录 前言1. 树的基本概念2. 二叉树的基本概念3. 特殊二叉树&#x1f351; 满二叉树&#x1f351; 完全二叉树&#x1f351; 斜树 4. 二叉树的性质&#x1f351; 性质一&#x1f351; 性质二&#x1f351; 性质三&#x1f351; 性质四&#x1f351; 性质五&#x1f351; 性…

华为EC6108V9E/EC6108V9I_rk3228_安卓4.4.4_通刷_卡刷固件包

华为EC6108V9E&#xff0f;EC6108V9I_rk3228_安卓4.4.4_通刷_卡刷固件包-内有教程 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简内置的…

【Java】线程池--ThreadPoolExecutor底层原理源码的理解

文章目录 一、根据代码查看jdk提供的3种线程池创建&#xff1a;二、3种方式源码分析1、Executors.newCachedThreadPool()2、Executors.newFixedThreadPool(10)3、Executors.newSingleThreadExecutor() 三、自定义方式执行提交优先级执行优先级 一、根据代码查看jdk提供的3种线程…

第2章Elasticsearch入门

1.正排索引和倒排索引 正排索引: 正排索引&#xff08;Forward Index&#xff09;是一种用于搜索引擎和文本检索系统的索引结构&#xff0c;它将文档中的每个单词都映射到该单词在文档中出现的位置。正排索引可以帮助快速定位、检索和渲染文档内容&#xff0c;但它需要消耗大…

AI 工具合辑盘点(十一)持续更新 之 AI 学术研究工具

许多学生和研究人员已经在利用人工智能进行研究。它可以让你更容易地了解最新研究成果&#xff0c;并帮助你组织和正确引用你最喜爱的研究论文。 从生成长篇研究论文摘要到通知你领域内的新趋势&#xff0c;研究中的AI工具节省了大量时间和精力。如果你在学术界&#xff0c;那…

数据库系统

目录 第三章、数据库系统1、数据库模式1.1、三级模式--两级映射1.2、数据库设计过程 2、ER模型3、关系代数与元组演算4、规范化理论4.1、函数依赖4.2、价值与用途4.3、键4.4、范式4.5、模式分解 5、并发控制6、数据库完整性约束7、数据备份8、数控故障与恢复9、数据仓库与数据挖…

网络安全合规-数据安全风险评估

一、法律依据&#xff1a; 依据《数据安全法》第三十条的规定&#xff0c;重要数据的处理者应当按照规定对其数据处理活动定期开展风险评估&#xff0c;并向有关主管部门报送风险评估报告。 依据《网络数据安全管理条例》&#xff08;征求意见稿&#xff09; 第三十二条规定&am…

实时通信的服务器推送机制 EventSource(SSE) 简介,附 go 实现示例

简介 不知道大家有没有见过 Content-Type:text/event-stream 的请求头&#xff0c;这是 HTML5 中的 EventSource 是一项强大的 API&#xff0c;通过服务器推送实现实时通信。 与 WebSocket 相比&#xff0c;EventSource 提供了一种简单而可靠的单向通信机制&#xff08;服务器…

《Linux 内核设计与实现》03. 进程管理

文章目录 进程描述符及任务结构分配进程描述符进程描述符的存放进程状态设置当前进程状态进程上下文进程家族树 进程创建线程在 Linux 中的实现创建线程内核线程 进程终结删除进程描述符孤儿进程 进程描述符及任务结构 内核把进程存放在任务队列&#xff08;task list&#xf…

MySQL高级--锁

一、锁 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题…

新手必看:腾讯云轻量服务器使用限制

腾讯云轻量应用服务器和云服务器CVM相比具有一些限制&#xff0c;比如轻量服务器不支持更换内网IP地址&#xff0c;轻量服务器只能套餐整体升级且不支持降配&#xff0c;轻量不支持用户自定义配置私有网络VPC&#xff0c;还有如实例配额、云硬盘配额、备案限制和内网连通性等限…

整理一下最近了解到的AIGC工具

AIGC工具的一点整理 前言AIGC类型图像生成类Stable diffusionMidjourneyDALLE 2三种工具比较DeepFloyd IF 文本生成语音生成So-vits-svc 4.0 结尾 前言 好久没有写csdn博客了&#xff0c;突然不知道写点什么&#xff0c;最近AIGC真的很火&#xff0c;有一种三天不看就跟不上发…