知乎日报——第四周

用msyql实现日报、周报、年报 在做一些业务的时候,我们有时间要做订单的日报、周报、年报 阅读详情

「OC」知乎日报——第四周(完)

本周总结

本周使用FMDB完成了本地数据的创建,管理相关的点赞收藏信息,优化了tableView,使用高度数组存储动态cell的高度,并且在展开之后更新动态高度数组,使用抽屉视图写了收藏新闻的展示页面。完成了离线缓存功能,在网络请求受阻的情况下,可以使用缓存当中的信息,这样加载过的信息在没有网络的情况下也可以获取信息。

请添加图片描述

收藏界面

首先收藏界面使用的是抽屉视图,使用的是present模态视图的方法,具体实现有兴趣的读者可以通过「iOS」自定义Modal转场——抽屉视图的实现进行相关的学习。

我们讲一下其中实现本地化存储的FMDB的Manger之中的相关逻辑,首先是和网络请求的思路类似,先设置一个管理类单例来使用FMDB进行管理,这是它的头文件。

#import <Foundation/Foundation.h>
#import "fmdb/FMDB.h"
NS_ASSUME_NONNULL_BEGIN
@class extraInfo;
@interface DBTool : NSObject
-(void)createDB;
-(void)insertInfo:(extraInfo *)info;
- (NSArray<extraInfo *> *)fetchStarInfo;
- (extraInfo *)fetchInfoForNewsID:(NSString *)newsID;
- (void)deleteInfoForNewsID:(NSString *)newsID;
+ (instancetype)sharedManager;
@end

NS_ASSUME_NONNULL_END

可以看到实现的内容也是数据库当中最基础的增删改查,由于不太难,我就直接把实现的方法代码贴出:

#import "DBTool.h"
#import "extraInfo.h"
@interface DBTool ()
@property (strong, nonatomic) FMDatabase *db;
@end

@implementation DBTool

+ (instancetype)sharedManager {
    static DBTool *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[DBTool alloc] init];
        [manager createDB];
        [manager createTab];
    });
    return manager;
}

- (void)createDB {
    NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString *fileName = [docPath stringByAppendingPathComponent:@"newsInfo.db"];
    NSLog(@"%@",docPath);
    self.db = [FMDatabase databaseWithPath:fileName];
    BOOL isSuccess = [self.db open];
    if (!isSuccess) {
        NSLog(@"打开数据库失败");
    }
}

- (void)createTab {
    [self createDB];
    NSString *sql = @"CREATE TABLE IF NOT EXISTS newsInfo ("
                    "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                    "newsID TEXT UNIQUE, "
                    "isLiked INTEGER, "
                    "isFavorited INTEGER, "
                    "newsTopic TEXT, "
                    "newsIamge TEXT)";
    BOOL isSuccess = [self.db executeUpdate:sql];
    if (!isSuccess) {
        NSLog(@"数据表创建失败: %@", [self.db lastErrorMessage]);
    }
    [self.db close];
}

- (void)insertInfo:(extraInfo *)info {
    [self createDB];
    NSString *sql = @"INSERT OR REPLACE INTO newsInfo "
                    "(newsID, isLiked, isFavorited, newsTopic, newsIamge) "
                    "VALUES (?, ?, ?, ?, ?)";
    BOOL isSuccess = [self.db executeUpdate:sql,
                      info.newsID,
                      @(info.isLiked),
                      @(info.isFavorited),
                      info.newsTopic,
                      info.newsIamge];
    if (!isSuccess) {
        NSLog(@"数据插入或更新失败: %@", [self.db lastErrorMessage]);
    }
    [self.db close];
}

- (extraInfo *)fetchInfoForNewsID:(NSString *)newsID {
    [self createDB];
    NSString *sql = @"SELECT isLiked, isFavorited, newsTopic, newsIamge FROM newsInfo WHERE newsID = ?";
    FMResultSet *result = [self.db executeQuery:sql, newsID];
    extraInfo *info = nil;
    if ([result next]) {
        info = [[extraInfo alloc] init];
        info.newsID = newsID;
        info.isLiked = [result boolForColumn:@"isLiked"];
        info.isFavorited = [result boolForColumn:@"isFavorited"];
        info.newsTopic = [result stringForColumn:@"newsTopic"];
        info.newsIamge = [result stringForColumn:@"newsIamge"];
    } else {
        NSLog(@"未找到新闻 ID 为 %@ 的记录", newsID);
    }
    [self.db close];
    return info;
}

- (NSArray<extraInfo *> *)fetchStarInfo {
    [self createDB];
    NSString *sql = @"SELECT id, newsID, isLiked, isFavorited, newsTopic, newsIamge FROM newsInfo WHERE isFavorited = 1 ORDER BY id DESC";
    FMResultSet *result = [self.db executeQuery:sql];
    NSMutableArray<extraInfo *> *infoArray = [NSMutableArray array];
    while ([result next]) {
        extraInfo *info = [[extraInfo alloc] init];
        info.newsID = [result stringForColumn:@"newsID"];
        info.isLiked = [result boolForColumn:@"isLiked"];
        info.isFavorited = [result boolForColumn:@"isFavorited"];
        info.newsTopic = [result stringForColumn:@"newsTopic"];
        info.newsIamge = [result stringForColumn:@"newsIamge"];
        [infoArray addObject:info];
    }
    [self.db close];
    return infoArray;
}

- (void)deleteInfoForNewsID:(NSString *)newsID {
    [self createDB];
    NSString *sql = @"DELETE FROM newsInfo WHERE newsID = ?";
    BOOL isSuccess = [self.db executeUpdate:sql, newsID];
    if (isSuccess) {
        NSLog(@"删除新闻 ID %@ 的记录成功", newsID);
    } else {
        NSLog(@"删除记录失败: %@", [self.db lastErrorMessage]);
    }
    [self.db close];
}

@end#import "DBTool.h"
#import "extraInfo.h"
@interface DBTool ()
@property (strong, nonatomic) FMDatabase *db;
@end

@implementation DBTool

+ (instancetype)sharedManager {
    static DBTool *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[DBTool alloc] init];
        [manager createDB];
        [manager createTab];
    });
    return manager;
}

- (void)createDB {
    NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString *fileName = [docPath stringByAppendingPathComponent:@"newsInfo.db"];
    NSLog(@"%@",docPath);
    self.db = [FMDatabase databaseWithPath:fileName];
    BOOL isSuccess = [self.db open];
    if (!isSuccess) {
        NSLog(@"打开数据库失败");
    }
}

- (void)createTab {
    [self createDB];
    NSString *sql = @"CREATE TABLE IF NOT EXISTS newsInfo ("
                    "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                    "newsID TEXT UNIQUE, "
                    "isLiked INTEGER, "
                    "isFavorited INTEGER, "
                    "newsTopic TEXT, "
                    "newsIamge TEXT)";
    BOOL isSuccess = [self.db executeUpdate:sql];
    if (!isSuccess) {
        NSLog(@"数据表创建失败: %@", [self.db lastErrorMessage]);
    }
    [self.db close];
}

- (void)insertInfo:(extraInfo *)info {
    [self createDB];
    NSString *sql = @"INSERT OR REPLACE INTO newsInfo "
                    "(newsID, isLiked, isFavorited, newsTopic, newsIamge) "
                    "VALUES (?, ?, ?, ?, ?)";
    BOOL isSuccess = [self.db executeUpdate:sql,
                      info.newsID,
                      @(info.isLiked),
                      @(info.isFavorited),
                      info.newsTopic,
                      info.newsIamge];
    if (!isSuccess) {
        NSLog(@"数据插入或更新失败: %@", [self.db lastErrorMessage]);
    }
    [self.db close];
}

- (extraInfo *)fetchInfoForNewsID:(NSString *)newsID {
    [self createDB];
    NSString *sql = @"SELECT isLiked, isFavorited, newsTopic, newsIamge FROM newsInfo WHERE newsID = ?";
    FMResultSet *result = [self.db executeQuery:sql, newsID];
    extraInfo *info = nil;
    if ([result next]) {
        info = [[extraInfo alloc] init];
        info.newsID = newsID;
        info.isLiked = [result boolForColumn:@"isLiked"];
        info.isFavorited = [result boolForColumn:@"isFavorited"];
        info.newsTopic = [result stringForColumn:@"newsTopic"];
        info.newsIamge = [result stringForColumn:@"newsIamge"];
    } else {
        NSLog(@"未找到新闻 ID 为 %@ 的记录", newsID);
    }
    [self.db close];
    return info;
}

- (NSArray<extraInfo *> *)fetchStarInfo {
    [self createDB];
    NSString *sql = @"SELECT id, newsID, isLiked, isFavorited, newsTopic, newsIamge FROM newsInfo WHERE isFavorited = 1 ORDER BY id DESC";
    FMResultSet *result = [self.db executeQuery:sql];
    NSMutableArray<extraInfo *> *infoArray = [NSMutableArray array];
    while ([result next]) {
        extraInfo *info = [[extraInfo alloc] init];
        info.newsID = [result stringForColumn:@"newsID"];
        info.isLiked = [result boolForColumn:@"isLiked"];
        info.isFavorited = [result boolForColumn:@"isFavorited"];
        info.newsTopic = [result stringForColumn:@"newsTopic"];
        info.newsIamge = [result stringForColumn:@"newsIamge"];
        [infoArray addObject:info];
    }
    [self.db close];
    return infoArray;
}

- (void)deleteInfoForNewsID:(NSString *)newsID {
    [self createDB];
    NSString *sql = @"DELETE FROM newsInfo WHERE newsID = ?";
    BOOL isSuccess = [self.db executeUpdate:sql, newsID];
    if (isSuccess) {
        NSLog(@"删除新闻 ID %@ 的记录成功", newsID);
    } else {
        NSLog(@"删除记录失败: %@", [self.db lastErrorMessage]);
    }
    [self.db close];
}

@end

我的存储结构就是,存储对应的序列ID,新闻ID,点赞收藏情况,新闻标题和新闻图片,由于点赞的数量其实需要实时申请,因此不做存储,在详情页的滚动视图到对应位置才进行申请。在详情页之中更新对应的BottomView的代码需要做出更改,内容大致如下:

 if (index == self.currentPage) {
    extraInfo *cachedInfo = [self getCachedInfoForStory:story];
    
    // 先设置界面上的 UI 状态
    [self updateBottomViewWithInfo:cachedInfo];

    // 如果缓存中有 extraInfo,且不需要网络请求,则不发送网络请求
    if (![self isInfoStale:cachedInfo]) {
        return;
    }
    
    // 发送网络请求,获取最新的 extraInfo
    [[NetworkManager sharedManager] fetchNewsExtraInfo:story.id completion:^(extraInfo *info, NSError *error) {
        if (!error && info) {
            [self updateInfo:info forStory:story];
            [self updateBottomViewWithInfo:info];
        } else {
            NSLog(@"Error fetching extra info: %@", error);
        }
    }];
}

- (extraInfo *)getCachedInfoForStory:(Story *)story {
    // 1️⃣ 优先从内存缓存中获取
    extraInfo *cachedInfo = self.extraInfoCache[story.id];
    
    if (!cachedInfo) {
        // 2️⃣ 如果内存缓存中没有,则从数据库中获取
        cachedInfo = [[DBTool sharedManager] fetchInfoForNewsID:story.id];
    }
    
    if (!cachedInfo) {
        // 3️⃣ 如果数据库中也没有,则创建默认的 extraInfo
        cachedInfo = [[extraInfo alloc] init];
        cachedInfo.newsID = story.id;
        cachedInfo.newsTopic = story.title;
        cachedInfo.newsIamge = [story.images firstObject];
        cachedInfo.isLiked = NO;
        cachedInfo.isFavorited = NO;
        
        // ⚡️ 将新创建的缓存对象加入内存缓存,避免重复创建
        self.extraInfoCache[story.id] = cachedInfo;
    }
    
    return cachedInfo;
}


- (void)updateBottomViewWithInfo:(extraInfo *)info {
    if (!info) return; // 防止空数据
    
    // 只更新 UI 一次,避免多次 setSelected
    [self.bottomView setInfo:info];
    [self.bottomView.like setSelected:info.isLiked];
    [self.bottomView.star setSelected:info.isFavorited];
}

这里拿获取详情页的状态的相关数据来举例,首先是是查找字典缓存之中的内容,如果找不到,其次再查找本地数据库之中的内容,如果都找不到就给点赞收藏状态给一个初始值NO。最后再在进行网络请求点赞评论数量时再进行赋值。

通过收藏界面进入的详情页和无限右滑的详情页的区别就是,收藏页的滚动页数是固定的,也没有太大的区别。

使用高度数组优化

我们在编写评论区的时候,由于我们的高度时动态变化的,由于cell的复用机制我们会不断的计算cell之中的高度,所以我们可以通过方法来计算cell的高度,然后将高度存储在C层的字典当中,当滑动到对应cell时可以根据index访问到cell对应的高度。

在这里我使用了一个类方法,去计算cell的高度

+ (CGFloat)heightForComment:(ShortComment *)comment {
    CGFloat height = 80;
    UIFont *usernameFont = [UIFont boldSystemFontOfSize:18];
    UIFont *commentFont = [UIFont systemFontOfSize:16];

    CGSize maxTextWidth = CGSizeMake(310, CGFLOAT_MAX);

    CGRect usernameRect = [comment.author boundingRectWithSize:maxTextWidth
                                                       options:NSStringDrawingUsesLineFragmentOrigin
                                                    attributes:@{NSFontAttributeName: usernameFont}
                                                       context:nil];
    height += usernameRect.size.height;
    CGRect commentTextRect = [comment.content boundingRectWithSize:maxTextWidth
                                                           options:NSStringDrawingUsesLineFragmentOrigin
                                                        attributes:@{NSFontAttributeName: commentFont}
                                                           context:nil];
    height += commentTextRect.size.height;
    if (comment.replyTo) {
        UIFont *replyFont = [UIFont systemFontOfSize:14];
        NSString *replyText = [NSString stringWithFormat:@"// %@:%@", comment.replyTo.author, comment.replyTo.content];
        if (comment.isExpanded) {
            
            CGRect replyTextRect = [replyText boundingRectWithSize:maxTextWidth
                                                           options:NSStringDrawingUsesLineFragmentOrigin
                                                        attributes:@{NSFontAttributeName: replyFont}
                                                           context:nil];
            height += replyTextRect.size.height;
        } else {
            CGFloat lineHeight = replyFont.lineHeight;
            CGFloat maxHeight = lineHeight * 3; 
            CGRect replyTextRect = [replyText boundingRectWithSize:maxTextWidth
                                                           options:NSStringDrawingUsesLineFragmentOrigin
                                                        attributes:@{NSFontAttributeName: replyFont}
                                                           context:nil];
            height += MIN(replyTextRect.size.height, maxHeight);
        }
    }
    return height;
}

我将固定的高度累加起来,然后再去动态计算两个textView的高度,计算完后将对应高度存在C层当中,代码逻辑如下

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSNumber *commentID = nil;

    // 确定评论的 ID
    if (indexPath.section == 0 && self.longComments.count > 0) {
        commentID = @(self.longComments[indexPath.row].commentId);
    } else if ((indexPath.section == 1 || (indexPath.section == 0 && self.longComments.count == 0)) && self.shortComments.count > 0) {
        commentID = @(self.shortComments[indexPath.row].commentId);
    }

    NSNumber *cachedHeight = self.heightCache[commentID];
    if (cachedHeight) {
        return cachedHeight.floatValue;
    }
    CGFloat height = 0.0;
    if (indexPath.section == 0 && self.longComments.count > 0) {
        LongComment* comment = self.longComments[indexPath.row];
        height = [LongCommentTableViewCell heightForComment:comment];
    } else if ((indexPath.section == 1 || (indexPath.section == 0 && self.longComments.count == 0)) && self.shortComments.count > 0) {
        ShortComment *comment = self.shortComments[indexPath.row];
        height = [CommentTableViewCell heightForComment:comment];
    }
    self.heightCache[commentID] = @(height); // 缓存高度
    return height;
}

还有一个问题就是,我们要保存我们展开的状态,所以如果我们点击展开,其实cell的高度会随着变化,但是在我们C层当中的数组存储的其实还是未展开时的高度,在这里我使用一个通知传值,当我们点击展开的时候,我们的cell发送通知给C层,根据打包的数据找到对应的cell,将高度字典之中的高度进行一个更新操作,具体操作如下:

//更改数据源之中的isExpanded属性,以及移除高度数组进行重新计算
-(void)reloadCell1:(NSNotification *)notification {
    CommentTableViewCell* cell = notification.userInfo[@"cell"];
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
    NSNumber *commentID = notification.userInfo[@"commentID"];
    ShortComment *commment =  self.shortComments[indexPath.row];
    commment.isExpanded = !commment.isExpanded;
    commment.isLike = [notification.userInfo[@"isLike"] boolValue];
    [self.tableView beginUpdates];
    if (commentID) {
           [self.heightCache removeObjectForKey:commentID];
       }
    [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.tableView endUpdates];
}

-(void)reloadCell2:(NSNotification *)notification {
    LongCommentTableViewCell* cell = notification.userInfo[@"cell"];
    NSNumber *commentID = notification.userInfo[@"commentID"];
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
    LongComment *commment = self.longComments[indexPath.row];
    commment.isLike = [notification.userInfo[@"isLike"] boolValue];
    commment.isExpanded = !commment.isExpanded;
    [self.tableView beginUpdates];
    if (commentID) {
           [self.heightCache removeObjectForKey:commentID];
       }
    [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.tableView endUpdates];
}

设置缓存

由于关于UrlCache之中的内容其实了解的不是很深,但所幸其实使用起来也不太难,只要对之前的AFN的manger之中的方法进行一点改修就可以了,接下来就介绍一下我实现的缓存机制

首先是在AppDelegate之中,我们给这个程序划分一个全局的内存,并给上对应的关键字,作为缓存

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSUInteger memoryCapacity = 50 * 1024 * 1024; // 50 MB 内存缓存
    NSUInteger diskCapacity = 200 * 1024 * 1024;  // 200 MB 磁盘缓存
    NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:@"networkCache"];
    [NSURLCache setSharedURLCache:urlCache];
    return YES;
}

紧接着在对应的网络请求方法之中,修改一下

- (void)fetchLatestNewsWithCompletion:(void (^)(News *response, NSError *error))completion {
    NSString *urlString = @"https://news-at.zhihu.com/api/4/stories/latest";
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    [self GET:urlString parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSDictionary *userInfo = @{@"cachedDate": [NSDate date]};
        NSData *data = [NSJSONSerialization dataWithJSONObject:responseObject options:0 error:nil];
        NSCachedURLResponse *newCache = [[NSCachedURLResponse alloc] initWithResponse:task.response data:data userInfo:userInfo storagePolicy:NSURLCacheStorageAllowed];
        [[NSURLCache sharedURLCache] storeCachedResponse:newCache forRequest:request];
        //当我在网络请求获取到信息的时候,就将对应信息存在缓存当中
                
        News *newsResponse = [News yy_modelWithJSON:responseObject];
        
        if (completion) {
            completion(newsResponse, nil);
        }
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    //在网络申请失败的时候,根据网络请求找到对应的缓存,将内容
        NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
        if (cachedResponse) {
            NSDictionary *userInfo = cachedResponse.userInfo;
            NSDate *cachedDate = userInfo[@"cachedDate"];
            NSTimeInterval cacheAge = [[NSDate date] timeIntervalSinceDate:cachedDate];
            News *newsResponse = [News yy_modelWithJSON:cachedResponse.data];
            if (completion) {
             ![请添加图片描述](https://i-blog.csdnimg.cn/direct/a2ffe5ed3fee425ca38b93e95a5b6904.gif)
   completion(newsResponse, nil);
            }
            return;
         }
        if (completion) {
            completion(nil, error);
    
        }
    }];
}

这样就算我们将网络关闭,程序还是会对我们的之前访问过的内容有一个缓存。

总结

到此为止,知乎日报的内容已经大致完成,剩下一些细节还需要慢慢的完善,通过知乎日报这个项目,确实逐步了解了如何用MVC的架构去写一个稍微完整的项目,学习了许多新的知识以及第三方库。当然过程之中仍然暴露了许多之前从未想过到的问题,还是认识到了自身的不足,还是希望自己能够在学习路上越来越精进自己。

ESD 防护深度解析:模型、等级、系统设计与防护策略 本文系统阐述了静电放电(ESD)防护的核心技术体系。首先对比分析了HBM(人体模型)和CDM(充电设备模型)两种基础ESD模型的物理特性、测试标准及分级要求,指出相同电压下CDM的破坏性是HBM的15-20倍。其次详细解析了IEC61000-4-2系统级ESD标准,包括接触/空气放电测试方法及判定等级。重点提出了系统级防护的三层架构:空间隔离(≥10mm间距)、电气隔离(地平面分割)和TVS管选型(响应时间<1ns),并对比了MOV、SCR等防护器件特性。针对不同应用场景(消费电子、工业控制等)给出了差异化 阅读详情

相关推荐

逆变器专题(3)-锁相环-PLL

锁相环作为并网逆变器的第一道门户,其锁相的精度将直接影响逆变器是否并网成功,若锁相精度较差,则会直接导致系统的功率因数下降,产生无功等等。在有关文献中指出,提高PLL带宽可以准确且快速进行锁相,而当电网电压含有谐波时,可适当降低带宽抵制谐波的干扰。成为变化量,而如果两者旋转速度达到一致时,所在的交轴分量。ki很大,即带宽很大时,稳定裕度不够,锁相环锁不住。为交流量,当频率锁定而相位没有锁定时,即。,即控制环前馈,可实现将锁相速度的加快。控为0,则可实现系统完美锁相。mod,取余,可减少存储空间。

cyc19980105的博客 6180

【iOS】知乎日报第四周总结

这里首页获取三个数据混乱的原因主要还是因为网络请求的异步回调的问题,这里笔者上网搜索了一下相关内容后面采用了一个这个类和一个并行队列来解决,这个类是状态锁,这里笔者还不是很清楚原因,这里可以简单介绍一下状态锁的一个内容:这里笔者给出我下滑加载的部分代码: FMDB库的一个使用 这里笔者实现收藏中心采用了一个第三方库FMDB这个库来实现的,首先这个库的是用OC来包装sqlite这个小型数据库,然后我们只需要调用她分装好的一个接口就可以了,这里笔者简单介绍一下使用方法,不涉及原理。创建一个库主要有三种方式来创建

qq_73106050的博客 1078

PyMuPDF`库实现PDF旋转功能

本文介绍了一个简单的Python应用程序,用于将PDF文件转换为旋转90度的PDF文件。通过使用`wx很抱歉,Markdown格式在当前聊天界面无法显示。我将为您提供纯文本格式的博客内容,您可以将其复制到Markdown编辑器中进行格式化。PDF转换工具本文介绍了一个简单的Python应用程序,用于将PDF文件转换为旋转90度的PDF文件。该工具使用了wxPython和PyMuPDF库。功能该PDF转换工具具有以下功能:1. 选择PDF文件:通过点击“选择文件”按钮,用户可以选择要转换的PDF文件。

Winfredzhang的博客 2733

[iOS开发]知乎日报第四周总结

摆烂

weixin_52192405的博客 1677

SVG文件上传漏洞实战:从XSS攻击到防御策略

本文深入剖析了SVG文件上传漏洞的成因与危害,揭示了攻击者如何利用SVG的XML本质构造恶意文件,绕过常规检查实现存储型XSS攻击。文章通过实战复现演示了完整的攻击链,并提供了从严格内容检查、安全解析过滤到设置CSP策略等多层次的纵深防御方案,帮助开发者从根本上加固文件上传功能。

weixin_29230805的博客 395

Plant Simulation数字孪生实战:从零搭建生产车间模型(附SimTalk脚本示例)

本文详细介绍了如何使用Plant Simulation从零构建生产车间数字孪生模型,包括基础建模、SimTalk脚本编程、参数化设计和高级应用。通过实战示例展示动态逻辑控制、设备故障模拟等关键技术,帮助工程师快速掌握数字孪生技术在制造业中的应用。

weixin_42527160的博客 357

知乎日报第四周总结

已完成: 完成了评论部分的内容,并且实现了长评论回复的展开与收起 评论功能的实现思路是:基础的方法是使用Masonry第三方库,按照总评论的个数创建一个数组,数组的值全部初始为1,在建立评论的cell时先判断网络请求来的数据里是否有回复部分,若有就加载回复相关的部分,每加载一个cell根据cell的indexPath.row判断数组中相应下标的值是否为1,若是1,就显示成收起的状态,若是2,就显示成展开的状态。其中,评论实现的难点就是cell的复用问题 学到的内容: 加深理解了tableView的复

m0_52192682的博客 4145

madgwick算法基于MPU9250姿态融合漂移问题的原因

基于MPU9250的madgwick算法姿态漂移问题原因 0、声明 因为这个是现象是不是共性目前本人还没研究过,只是在个体问题研究的基础上得出的结论,或许我买到的MPU9250是一个盗版:),所以只具备参考性。 1、现象 使用MPU9250的加速度计、角速度计、磁力计(AK8963)原始数据输入到madgwick算法中,得到的姿态最终会在停止姿态变化后仍然漂移一小段时间。其中加速度计和角速度计读取速率为400hz,磁力计为100hz,加速度计采样范围±2g,角速度计采样范...

GreedyZombie的博客 3872

iOS—知乎日报第四周总结

iOS知乎日报小结

cyberjack的博客 864

基于 YOLOv26 的 PCB 板缺陷检测:工业级视觉实战

本文提出了一种基于YOLOv26的PCB板缺陷检测系统,通过深度学习技术实现对PCB板缺陷的自动化检测。系统能够识别短路、开路、焊点不良等8种常见缺陷,具有高精度、多类别分类和实时推理等优势。文章详细介绍了系统架构,包括数据采集、预处理、YOLOv26模型检测和后处理模块。在代码实现部分,给出了环境配置、数据预处理与增强的具体方法,使用Albumentations库进行图像增强处理。该系统为电子制造业提供了高效、准确的PCB质量检测解决方案,显著提升了检测效率和准确性。

一键难忘的博客 335

6、Dialogflow 开发全解析:从基础到高级应用

本文全面解析了 Dialogflow 的核心功能与高级应用,涵盖意图与实体的创建、上下文管理、后续意图设置以及高级实体类型的实际应用场景。详细介绍了自定义实体的导入导出规则、编程添加方法及数量限制,并通过流程图和实例说明了上下文生命周期与对话连贯性设计。结合电商、旅游、问卷调查等实际业务场景,展示了模糊匹配、自动扩展、复合实体等功能的强大灵活性。最后提供了开发最佳实践建议,帮助开发者构建高效、智能的对话系统。

kafka6courier的博客 103

知乎日报仿写--第四周

这两周完成的东西比较少,有金工实习的原因也有自身的一些原因,但尽量下周完成知乎日报的仿写;这两周完成了评论区数据的请求,cell高度的计算,cell高度自适应,以及对以前bug的修复;

m0_74217598的博客 211

(三十九)Flask 多应用架构(Multiple Apps)与 DispatcherMiddleware 实战解析

(三十九)Flask 多应用架构(Multiple Apps)与 DispatcherMiddleware 实战解析

孤寒者的博客 1万+

仿写知乎日报第四周

本周主要修改了以往的一些bug,实现了一些遗漏的新功能。

m0_73348697的博客 370

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

【iOS-知乎日报第四周总结】

完成的潦潦草草,对于已经加载数据的缓冲我是把所有的都去加载一遍在去显示webVIew,这样的做法是很耗费性能的,一会儿内存耗费就上去了,这个问题值得研究改进!

weixin_61639290的博客 761

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

【iOS】—— 知乎日报第四周遇到的问题

1.imageWithData获取图片为nil: imageWithData 使用分析

m0_55124878的博客 2276

[iOS]--知乎日报第四周总结

可以通过Massonry来实现效果,可以借助 UITableView 的 estimatedRowHeight 和 rowHeight 属性,以及 Masonry 的自动布局功能。通常对数据库的操作,我们一般称为CURD,即对表进行创建(Create)、更新(Update)、读取(Read)和删除(Delete)操作。首先,确保你的 UITableViewCell 中的子视图使用了 Masonry 进行自动布局。

qq_72437394的博客 287

知乎日报--第四周

我这里需要让cell的第一个section的高度为0,其他的section的高度相同。我这里的导航栏使用的是一个自定义的view来做了一个假导航栏。这行代码的意思就是跳转到woDeView界面并取消导航栏。解决cell的复用问题后下拉刷新就解决了。之前一直没有解决cell复用的问题。但是有时候需要去掉这个导航栏。

weixin_61196797的博客 275

知乎日报---第四周总结

1.点进去的页面需要用到第一个页面的controller页面的数组,这里需要传值,要注意的是传值的顺序,应该是在第一个页面的C里进行页面跳转时,通过属性传值将它的数组传给点进去的页面的C里的数组,然后再由C传到它的V。2.在写点进去的设置这个页面时,用的是自定义cell,所以设置tableview类型时,不像非注册的cell呢样每组的下面会自动有间隔,为了自定义cell也显示出来呢个灰白色的间隔样式,我就在每组的下面加了footerView,这样就可以了。1.向下滑动刷新时显示小菊花代表加载。

weixin_61558111的博客 250

[iOS]知乎日报第四周总结-唐妞不等式秒了之没写完不等于不能发博客

[iOS]知乎日报第四周总结-唐妞不等式秒了之没写完不等于不能发博客

m0_74703932的博客 315

工作日报模板_天天写日报,到底有什么用途

关注并将「人人都是产品经理」设为星标每天早 07 : 45 按时送达要问数据分析师最讨厌的一件事是什么,很多人会回答是“日、周、月、季、年报制度”,这已经变成了天天都得干,干了还没啥用的鸡肋。为什么要搞这样一个制度呢?它的意义在哪里?本文作者将对此进行六个维度的分析,希望对你有帮助。全文共2953字,阅读需要 6分钟—————— BEGIN ——————日、周、月、季、年报制度,可...

weixin_39643865的博客 1485

第四周日报

1.已经理解的知识点:函数的定义,函数的封装,函数的调用。列表的定义,增删改查,列表的切片。完成3个案例:质数、股票分析系统、打地鼠系统。 2不理解的知识点:无 3.不会用的知识点:无 4.作业不会做的功能:无 5.上课有什么问题:无 6.今日心得总结: 函数可以使程序更加模块化,减少代码重复。函数定义后,如果不经过调用,不会被执行。列表用逗号将各个元素隔开,将元素包在[ ]中。通过对案例的练习,让我对函数的定义和调用有了更好的理解,线上上课注意力没办法很好集中,一不小心走神抬头已经懵圈。 ...

weixin_46427623的博客 303

2022-5-第四周日报

0 2022/5/23 1 Provably Secure Federated Learning against Malicious Clients代码复现 1.1 aggregator.py import numpy as np class FedAvg: def __init__(self, global_model, different_client_values, client_count): global_weights = np.array(global_model.

mental的博客 640
上一篇: 「iOS」通过CoreLocation Framework深入了解MVC架构
下一篇: Effective Objective-C 2.0 读书笔记——关联对象
小鹿撞出了脑震荡
博客等级 码龄3年 1107粉丝 97原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值