iOS ------ UIKit相关

UIView和CALayer

UIView

UIView表示屏幕上的一块矩形区域,它是基本上iOS中所有可视化控件的父类。UIView可以管理矩形区域里的内容,处理矩形区域的事件,包括子视图的管理以及动画的实现。

UIKit相关类的继承关系
在这里插入图片描述

UIView继承自UIResponder,所以UIView可以做事件响应,它也是所有视图(控件)直接或间接的父类

CALayer

CALayer继承于NSObject,我们称之为层,CALayer类的概念与UIView非常类似,可以包含图片、文本、背景色等。CALayer直接继承自NSObject,没有事件响应的功能。CALayer中包含API,可判断某点是否在图层范围内,但是没有响应链的存在

UIView和CALayer的关系

在每一个UIView实例当中,都有一个默认的支持图层layer,UIView负责创建并且管理这个图层。UIView因为里面有layer层,才具有显示的功能。UIView仅仅是对layer的一层封装,实现了CALayer的delegate,提供了处理事件交互的具体功能,还有动画底层方法的高级API。可以说,CALayer是UIView的内部实现细节。

UIView和CALayer的区别

1,UIView可以响应事件,CALayer不可以响应事件
2,一个 Layer 的 frame 是由它的 anchorPoint,position,bounds,和 transform 共同决定的,而一个 View 的 frame 只是简单的返回 Layer的 frame
3,UIView主要对显示的内容的管理而CALayer主要侧重显示内容的绘制
4,在做 iOS 动画的时候,修改非 RootLayer的属性(譬如位置、背景色等)会默认产生隐式动画,而修改UIView则不会。

UIView的CALayer类似于UIView的子view树状结构,也可以向它的layer上添加子layer,来完成某些特殊的表示

    UIView *firstView = [[UIView alloc] init];firstView.frame = CGRectMake(200, 200, 200, 200);firstView.backgroundColor = [UIColor redColor];[self.view addSubview:firstView];CALayer *layer = [[CALayer alloc] init];layer.backgroundColor = [[UIColor greenColor] CGColor];layer.position = CGPointMake(100,100);  //中心点layer.bounds = CGRectMake(100,100,80,80);[firstView.layer addSublayer:layer];

在这里插入图片描述

可以看出并没有在父视图添加新的图层,而是在view上改变了颜色,这和addSubView并不一样。可以看出layer是对View显示内容的绘制。

  • CAlayer视图结构类似于UIView的子View树形结构,可以在layer上添加子layer,类似于view添加view来实现一些特殊的表示。不同之处CALayer在添加的时候不会添加新视图,类似于修改原来的layer。
  • UIVIew的layer树形在系统内部,被系统维护三份copy
    • 第一份,逻辑树,代码可以在里面操作,例如通过代码更改layer的属性(比如frame\bounds)就在这一份进行操作
    • 第二份,动画树,这是一个中间层,系统在这一层更改属性,进行各种渲染操作
    • 第三份,显示树,这棵树的内容就是当前正被显示在屏幕上的内容

这三棵树的逻辑结构都是一样的,区别只是有各自的属性

UITableView

UITableView代理需要遵循的两个协议以及必须实现的方法

主要是通过2个协议:UITableViewDataSource和UITableViewDelegate

  • UITableView需要一个数据源代理(dataSource)来显示数据,UITableView会向数据源查询一共有多少行数据以及每一行显示生命数据。没有设置数据源的UITableView只是一个空壳。凡是遵循UITableViewDataSource协议的OC对象,都可以是UITableView的数据源
  • 我们也需要为UITableView设置代理对象(delegate),以便在UITableView触发某些事件时做出相应的处理。
  • 凡是遵循了UITableViewDelegate协议的OC对象,都可以是UITableView的代理对象,一般会让控制器充当充当UITableView的dataSource和delegate,我们可以手动实现协议中的某些方法来完成UITableView的实现。

@protocol UITableViewDataSource的方法

//返回组数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView//返回每组里的行数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section //cell的实现
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath // section头的title(例如,通讯录不同姓名标识)
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section// section尾段的title 
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section// section索引的title集合(例如,通讯录索引,帮助快速找到姓名)
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{}

@protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>的方法

//点击cell事件
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
// 设置cell行高(因为参数是indexPath,所以可以设置不同section的行高,也能设置同一section不容row的行高)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
// section头部的height
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
// section尾部的height
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
// section头部的view
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
//  section尾部的view
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section

其中代理对象必须实现的方法是DataSource协议的方法

//每组的cell
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
//实现cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

几个主要的方法在TableView加载时的执行顺序

  • numberOfSectionsInTableView
  • numberOfRowsInSection
  • cellForRowAtIndexPath
  • heightForRowAtIndexPath

TbaleView的cell的复用

在滑动tableView时为了避免cell的重复的销毁创建而消耗性能就出现了cell的复用

简单来说:当创建了多个cell且屏幕显示不了所有的cell,系统会将已经创建不在显示的cell放入复用池中,当需要新建cell并且标识符与复用池中的标识符存在相同的情况,就会调用复用池中的cell,并且使用该cell的所有cell控件

cell的两种复用机制

自定义cell

cell复用会出现的问题

对于不同种类的自定义cell,有时复用会出现问题。如果自定义cell上的控件不同,会出现复用到控件不同的cell,这时就会出现界面的错乱。

解决方法:

  • 弃用重用机制 - 从indexpath每次获取新的cell
  • 对于不同种类的cell设置不同的标识符,通过不同的标识符去复用相应类型的cell
  • 在prepareForReuses中重置所有的subView的显示属性为nil。(当前已经被分配的cell如果被重用了,会调用cell的prepareForReuse通知cell)

在这里插入图片描述

在这里插入图片描述

tableViewCell的行高计算

动态计算-缓存高度

动态计算

实际开发中,使用最多的应该是动态计算cell高度。例如标题高度不固定,标签不固定,这样就需要更具model里的内容计算行高

在这里插入图片描述

使用的时候在tableView的代理设置

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {WMSearchResultQAModel *model = self.dataArray[indexPath.row];return [WMSearchResultQAModel calutWholeCellHeightWithModel:model];
}

这样就可以达到每个cell根据内容展示不同高度的要求了。 这种方法很繁琐,但是也是最精确的,最可控的

缓存行高

当tableView滚动的时会不停的调用heightForAtIndexPath这个代理方法,当cell的高度需要自适应时,就意味着每次回调这个方法都要计算高度,而计算是非常消耗时间的,就会产生卡顿。

为了避免重复且无意义的计算cell高度,缓存高度就释放重要。

缓存高度机制

缓存高度需要一个容器来保存高度数值,可以是model ,一个可变数组,一个可变字典,以达到每当回调 heightForRowAtIndexPath 这个方法时,我们先去这个缓存里去取,如果有,就直接拿出来,如果没有,就计算高度,并且缓存起来。

以model为例

在model里声明个cellheight属性,用于保存Model对应的cell高度,然后在heightForRowAtIndexPath 方法中,如果当前model的cellHeight为0,说明这个cell没有缓存过高度,则计算Cell的高度,并把这个高度记录在model的cellHeight。如果当前model的cellHeight不为0,则直接使用。


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {WMSearchResultQAModel *model = self.dataArray[indexPath.row];if (model.cellHeight > 0) {// 有缓存的高度,取出缓存高度return model.cellHeight;}// 没有缓存时,计算高度并缓存起来CGFloat cellHeight; = [WMSearchResultQAModel calutWholeCellHeightWithModel:model];// 缓存给modelmodel.cellHeight = cellHeight;return cellHeight;
}
手动计算高度的两个方法
  • sizeToFit
  • boundingRectWithSize

sizeToFit

    UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(10,100, 350, 0)];label.numberOfLines = 0;label.text =@"当前视图的边界和边界大小的变化123123213213213123123123123123213213123213213213123123213";NSLog(@"the label bounds : %@",NSStringFromCGRect(label.frame));[label sizeToFit];NSLog(@"%f",label.frame.size.height);[self.view addSubview:label];

我们不设置高度,设置label.numberOfLines = 0;和[label sizeToFit];可以实现label的最多展开,并可获得相应的高度。

注意事项

  • 调整大小:sizeToFit 调整视图的大小,以适应内容。
  • 不改变位置:sizeToFit 不会更改视图的位置。
  • 确保内容已设置:调用 sizeToFit 之前,应设置好视图的内容。

doundingRectWithSize

返回文本会在所占据的矩形空间

CGRect rect=[(NSString *)obj boundingRectWithSize:CGSizeMake(1000, FONTHEIGHT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil].size.width;

里面的参数如下:

  • obj 是指要计算显示的字符串
  • boundingRectWithSize 表示计算的宽高限制
  • 计算高度时,需要宽度固定:CGSizeMake(1000, CGFLOAT_MAX)
    这里的1000也可以用已经确定的控件的宽度替代self.label.width
    计算结果表示在宽度最多为1000高度不限时,显示完全字符串需要的高度
    计算宽度时,需要高度固定:CGSizeMake(CGFLOAT_MAX, 200)
    同理200也可以用已知高度替换:self.label.height
    计算结果表示在高度不超过200时,将给定的字符串现实完全需要的宽度
  • options是文本绘制的附加选项
  • NSStringDrawingUsesLineFragmentOrigin 是默认基线
  • attributes字典格式,限定字符串显示的样式,一般限制字体较多,比如:@{NSFontAttributeName:[UIFont systemFontOfSize:16]}
    context包括一些信息,例如如何调整字间距以及缩放。最终,该对象包含的信息将用于文本绘制。一般写nil。
    UILabel * labelSecond = [[UILabel alloc] init];labelSecond.numberOfLines = 0;labelSecond.text =@"当前视图的边界和边界大小的变化123123213213213123123123123123213213123213213213123123213";labelSecond.font = [UIFont systemFontOfSize:16];//用我们预设的width来算label应该显示的size   CGRect rectTest = [labelSecond.text boundingRectWithSize:CGSizeMake(350, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil];//从刚才算的size中取出heigth//加1是怕四舍五入完之后少了一点导致显示的label少了一行CGFloat titleHeight = ceilf(rectTest.size.height) + 1;//用算出来的结果显示labellabelSecond.frame = CGRectMake(10,200, 350, titleHeight);//将label作为子视图添加到父视图[self.view addSubview:labelSecond];
自适应行高-缓存高度

在 iOS8 之后,系统结合autolayout提供了动态结算行高的方法 UITableViewAutomaticDimension,做好约束,我们都不用去实现 heightForRowAtIndexPath 这个代理方法了。

实现步骤
1,tableView设置

// 预设行高
self.tableView.estimatedRowHeight = xxx;
// 自动计算行高模式 
self.tableView.rowHeight = UITableViewAutomaticDimension;

2,在自定义cell,masonry布局

- (void)layoutSubviews {[super layoutSubviews];[self.headImgView mas_makeConstraints:^(MASConstraintMaker *make) {make.top.left.offset(kSpace15);make.size.mas_equalTo(CGSizeMake(50.f, 50.f));// 在自动计算行高模式下 要加上的 make.bottom.equalTo(self.contentView.mas_bottom).offset(-kSpace15);}];[self.nickNameLabel mas_makeConstraints:^(MASConstraintMaker *make) {make.left.equalTo(self.headImgView.mas_right).offset(12.f);make.top.offset(17.f);}];[self.jobWorkLabel mas_makeConstraints:^(MASConstraintMaker *make) {make.left.equalTo(self.nickNameLabel.mas_right).offset(8.f);make.right.lessThanOrEqualTo(self.contentView.mas_right).offset(-kSpace15);make.top.offset(21.f);}];[self.hospitalLabel mas_makeConstraints:^(MASConstraintMaker *make) {make.left.equalTo(self.headImgView.mas_right).offset(12.f);make.top.equalTo(self.jobWorkLabel.mas_bottom).offset(6.f);}];[self.line mas_makeConstraints:^(MASConstraintMaker *make) {make.left.right.bottom.offset(0);make.height.mas_equalTo(0.5f);}];
}
  • 所有子控件都要依赖self.contentView作为约束父控件
  • 关键控件要做buttom约束,确定好控件的上下边界,根据控件的动态内容将cell纵向撑开。

3,最关键的一步: [cell layoutIfNeeded]

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {WMDoctorEvaluateDescribeInputCell *cell = [tableView dequeueReusableCellWithIdentifier:[WMDoctorEvaluateDescribeInputCell reuseIdentifier] forIndexPath:indexPath];kWeakSelfcell.describeInputBlock = ^(NSString * _Nonnull describeText) {weakSelf.inputDescribeText = describeText;};//关键的一步,解决不正常显示问题[cell layoutIfNeeded];return cell;
}

缓存高度机制

首先获取cell实际显示的高度

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath 
{NSString *key = [NSString stringWithFormat:@"%ld",    (long)indexPath.row];[self.heightDict setObject:@(cell.height) forKey:key];NSLOG(@"第%@行的计算的最终高度是%f",key,cell.height);
}

didEndDisplayingCell当cell已经被正真的显示到屏幕上时会调用这个方法,此时的高度是cell的正真高度。根据indexPath.row作为key,将高度缓存进字典。

然后在 heightForRowAtIndexPath 方法里判断,如果字典里有值,则使用缓存高度,否则自动计算:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{NSString *key = [NSString stringWithFormat:@"%ld",indexPath.row];if (self.heightDict[key] != nil) {NSNumber *value = _heightDict[key];return value.floatValue;}return UITableViewAutomaticDimension;
}

ViewController的生命周期

ViewController相关函数以及执行顺序

当一个视图被创建,并且在屏幕上显示的时候

1.alloc 创建对象,分配空间
2. init; 初始化对象,初始化数据
3. loadview 在UIViewController对象的view被访问且为空的时候调用
4. viewDidLoad 控制器载入完成,可以进行自定义数据,以及动态创建其他控件
5. viewWillAppear 视图出现在屏幕之前,马上这个视图就会被展现在屏幕上了
6. viewWillLayoutSubviews 该方法在通知控制器将要布局 view 的子控件时调用
7. viewDidLayoutSubviews 该方法在通知控制器已经布局 view 的子控件时调用
8. viewDidAppear 视图已在屏幕上渲染完成

当一个视图被移除屏幕并且销毁的时候,代码执行的顺序

1,viewWillDisappear 视图将被从屏幕上移除之前执行
2,viewDidDisappear 视图已经被从屏幕上移除,用户看不见这个视图了
3,dealloc 视图被销毁,释放在init和viewDidLoad中创建的对象

离屏渲染

图片渲染和显示的流程

在这里插入图片描述

  • 在Application阶段,CPU会创建我们的视图,计算视图的一些数据,进行编解码,绘制纹理等操作然后交给GPU
  • GPU先通过顶点着色器去确定图像在硬件上的上的具体显示位置,然后通过片源着色器计算每个像素点的颜色值,最后通过光栅化找到像素点的位置,并把颜色显示上去。最终转化为一个个屏幕像素。
  • 然后把渲染后的数据放到帧缓存区(FrameBuffer),然后视图控制器就会读取数据交给显示器显示
掉帧

通过屏幕扫描的方式,会通过CRT电子枪从上到下逐行扫描,这个扫描的过程就是读取帧缓存区里的数据,当它扫描完的时候,它就会显示一帧的画面, 当显示完一帧画面后,CRT电子枪又回到原来的位置继续重新扫描显示下一帧。当一个垂直同步信号过来的时候,如果说CPUGPU还没有完成渲染的结果去做提交,也就是没有把数据放到FrameBuffer里面,这种情况,未过来提交过来这一帧的画面就会被丢弃,然后等待下一次垂直同步信号过来,再来显示新的画面,这个过程被称为掉帧

离屏渲染

当视图比较复杂的时候,GPU无法扫描视图的全部内容并把渲染数据反在FrameBuffer中,无法显示画面的全部内容,就需要开辟一块二外的内存缓存区,把剩下的渲染数据放入其中,再合并到FrameBuffer中,最后进行视图的显示。

导致离屏渲染的操作
  • 添加光栅化
  • 添加遮罩
  • 添加阴影
  • 抗锯齿
  • 设置背景颜色和圆角
  • 不透明

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云原生-利用容器和编排器

容器和编排器旨在解决单片部署方法中常见的问题。 1. 整体部署的挑战 传统上&#xff0c;大多数应用程序都是作为单个单元部署的。这样的应用程序被称为单体。这种将应用程序作为单个单元部署的一般方法&#xff08;即使它们由多个模块或程序集组成&#xff09;称为单体架构&…

OrangePi AIpro学习4 —— 昇腾AI模型应用

目录 一、ATC模型转换 1.1 模型 1.2 ATC工具 1.3 实操模型转换 1.4 使用ATC工具时的一些关键注意事项 1.5 ATC模型转换命令举例 二、运行昇腾AI模型应用样仓程序 2.1 程序目录 2.2 下载模型和模型转换 2.3 下载图片和编译程序 2.4 解决报错 2.5 运行程序 三、运行…

《决胜B端 产品经理升级之路》 知识点总结

什么是b端产品&#xff1f; b端产品是指面向企业或组织的经营管理问题&#xff0c;旨在解决企业规模、成本、效率、品质和风控等方面的产品。这些产品主要帮助企业提高运营效率、降低成本、改善品质和控制风险等。b端产品适用于各种行业和企业类型&#xff0c;可以为企业带来深…

Parallels Desktop 可以做什么?

现在 Mac 电脑在全球越来越火&#xff0c;但是很多时候我们还是需要用win上的老物件&#xff0c;也就导致越来越多的用户都想在 Mac 上跑 Windows 软件和游戏。虽说 MacOS 本身有不少厉害的应用程序&#xff0c;可有些 Windows 专用的软件和游戏在 MacOS 里还是没法直接跑。 黑…

视觉SLAM ch3—三维空间的刚体运动

如果对于某些线性代数的知识不太牢固&#xff0c;可以看一下我的另一篇博客&#xff0c;写了一些基础知识并推荐了一些视频。 旋转矩阵 单元所需的线代基础知识https://blog.csdn.net/Johaden/article/details/141023668 一、旋转矩阵 1.点、向量、坐标系 在数学中&…

线程池总结

一.线程池的概念 线程池是一种管理和复用线程的设计模式&#xff0c;主要用于提高多线程编程中的效率。它通过维护一组线程来执行多个任务&#xff0c;从而避免频繁地创建和销毁线程所带来的性能开销。 线程池里取线程比从系统中申请线程更高效的原因&#xff0c;也是因为线程池…

基于深度学习的太阳暗条检测(2020年以来)

A universal method for solar filament detection from Hα observations using semi-supervised deep learning A&A, 686, A213 (2024) A universal method for solar filament detection from Hα observations using semi-supervised deep learning (aanda.org) ABS…

七夕特辑:用Ta的照片定制专属二维码,传递独一无二的爱

七夕火热进行中&#xff0c;有人还在纠结送啥礼物合适么&#xff1f; 围观可能是“全网第一人”的技术助力七夕之特别礼物 &#xff01;&#xff01;&#xff01; 欢迎扫码关注围观。 七夕特辑&#xff1a;扫码解锁爱情故事&#xff0c;让爱穿越时空 七夕特辑&#xff1a;用…

猫头虎推荐:人类通向AGI之路 史上最重磅的20篇论文你值得学习

猫头虎推荐&#xff1a;人类通向AGI之路 史上最重磅的20篇论文你值得学习 &#x1f44b; 大家好&#xff0c;我是猫头虎&#xff0c;今天我们为大家带来一篇穿越时空的AI研究大作&#xff01;这篇文章将带你领略过去15年推动人工智能&#xff08;AI&#xff09;发展的20篇经典…

【驱动篇】龙芯LS2K0300之RTC设备驱动

实验介绍 本次实验是关于pcf8563 RTC模块的驱动移植&#xff0c;大致流程如下&#xff1a; 注册i2c设备驱动编写RTC设备驱动将device和driver驱动部署到开发板并装载&#xff0c;通过hwclock命令来测试 模块连接 VCC接Pin2&#xff0c;GND接Pin1&#xff0c;SCL接Pin16&…

比OpenAI的Whisper快50%,最新开源语音模型

生成式AI初创公司aiOla在官网开源了最新语音模型Whisper-Medusa&#xff0c;推理效率比OpenAI开源的Whisper快50%。 aiOla在Whisper的架构之上进行了修改采用了“多头注意力”机制的并行计算方法&#xff0c;允许模型在每个推理步骤中预测多个token&#xff0c;同时不会损失性…

略谈set与map的pair封装与进入哈希

引子&#xff1a;之前我们讲了红黑树的自实现&#xff0c;与小小的接口实现&#xff0c;那set与map的pair封装是如何实现的呢&#xff1f;&#xff0c;今天我们来一探究竟&#xff0c;而且我们也要进入新章节--哈希 对于operator--()的封装&#xff1a; 注意&#xff1a;牢记思…

一款.NET开发的AI无损放大工具

一款.NET开发的AI无损放大工具 思维导航 前言项目功能支持语言系统要求项目源代码项目运行小图片进行无损放大项目源码地址优秀项目和框架精选 前言 今天大姚给大家分享一款由.NET开源&#xff08;GPL-3.0 license&#xff09;、基于腾讯ARC Lab提供的Real-ESRGAN模型开发的A…

Linux知识复习第2期

RHCE 远程登录服务-CSDN博客 Linux 用户和组管理_linux用户和组的管理-CSDN博客 Linux 文件权限详解-CSDN博客 目录 1、sshd 免密登录 (1)纯净实验环境 (2)生成密钥 (3)上锁 2、用户管理 (1)添加新用户 (2)删除用户 (3)修改用户信息 (4)为用户账号设…

【Linux:环境变量】

目录 命令行参数&#xff1a; 环境变量&#xff1a; 命令行参数&#xff1a; argv是一个char*类型的数组&#xff0c;里面存放着字符、字符串的指针地址&#xff0c;且该数组必定是以NULL结尾 命令行中启动的进程都是Bash的子进程&#xff0c;命令行参数的存在本质上就是通过…

[qt] 多线程应用01

源码: 点击此处 一 多线程应用 实现一个多线程的网络时间服务器&#xff0c;利用多线程功能的技术&#xff0c;为每个客户端返回当前的时间&#xff0c;并且在返回后自动退出。同时&#xff0c;服务器也会记录当前受到的请求次数。其实这相当于一个ntp时间服务器 二 服务器实…

职场中,这些事情是禁忌

越级打报告 身处职场&#xff0c;一定要清晰地明确自己所处的位置。要了解部门的运营架构和人事结构&#xff0c;这是身为职场人对自己的最基本的要求。以此确保一旦工作中出现什么问题时&#xff0c;你能找到相应的负责人。但是这里一定要注意&#xff0c;千万不要故作聪明越…

【数据结构】顺序表实现

0. 前言 小伙伴们大家好&#xff0c;从今天开始&#xff0c;我们就开始学习《数据结构》这门课程~ 首先想给大家讲讲什么是数据结构&#xff1f; 0.1 数据结构是什么&#xff1f; 数据结构是由“数据”和“结构”两词组合⽽来。 什么是数据&#xff1f; 比如常⻅的数值1、…

【Material-UI】Button 中的点击事件处理(Handling clicks)详解

文章目录 一、点击事件处理基础1. 基本用法2. 事件处理器的传递 二、实际应用中的注意事项1. 事件处理逻辑的优化2. 避免过多的状态更新3. 使用合适的事件类型 三、关于文档中未提及的原生属性四、最佳实践1. 无障碍性2. 视觉反馈3. 防止重复点击 五、总结 在现代前端开发中&am…

【竞品分析】竞品分析的步骤

在产品经理的工作实际中,对产品的设计离不开竞品分析。 竞品分析可以辅助我们进行可行性评估、制定产品战略、优化产品迭代等。 可以说,竞品分析是贯穿产品生命周期的,是产品经理的必备专业技能。 个人认为&#xff0c;做自己家的产品是单一的视角&#xff0c;多做竞品分析会…