【OC总结 属性关键字】

news2024/11/24 22:58:04

文章目录

  • 前言
    • 1. 属性关键字的分类
    • 2. 内存管理关键字
      • 2.1 weak
      • 2.2 assgin
      • weak和assgin的区别
      • 2.3 strong
      • 2.4 copy关键字
        • copy关键字和strong的区别
        • 注意
      • 2.5 多种copy模式:copy 和 mutableCopy 对 容器对象 进行操作
      • 2.6 问题总结
    • 3. 线程安全的关键字 (nonatomic, atomic)
      • 3.1 nonatomic关键字
      • 3.2 atomic关键字
    • 4. 修饰变量的关键字 (const,static,extern)
      • 4.1 常量const
      • 4.2 常量修饰符(const)和宏定义(define)的区别:
      • 4.3 static
      • 4.4 常量extern
      • 4.4 static与const联合使用
      • 4.5 extern与const联合使用

前言

属性关键字尤为重要, 总结复习。

属性关键字就是用来修饰属性的关键字,保证了程序的正常运行。

1. 属性关键字的分类

  • 内存管理有关的关键字:weak, assgin, strong, retain, copy
  • 线程安全的关键字:monatomicatomic
  • 访问权限的关键字:readonlyreadwrite
  • 修饰变量的关键字:conststaticextern

2. 内存管理关键字

2.1 weak

weak经常用来OC对象类型的数据,修饰的对象在释放之后,指针地址会自动被置nil,这是弱引用的表现

  • ⚠️在ARC的环境下,为了避免循环引用,delegate往往也是weak修饰。在MRC下用assgin修饰。当某个对象不在拥有strong类型的指针指向的时候,对象就会被释放,即使还有weak指针类型指向他,weak指针也会被清除。

2.2 assgin

  • assgin常用于非指针变量,用于修饰基础数据类型和C的数据类型 & id类型的数据。用于基本数据类型进行复制操作。
  • asssgin不会修改引用计数,也可以用来修饰对象一般不建议如此,因为assgin修饰的对象被释放之后指针的地址还存着,成为了一个没有指向的野指针(垂悬指针)。

⚠️:assgin修饰的基本类型都是基本数据类型,基本数据类型分配在栈上的,栈上的变量是系统自动进行管理,不会造成野指针以及:MRC下的delegate往往assgin,此操作是为了deletage和self等自身产生循环引用。

eg:当对象A通过retain持有了B,B的delegate对象是A,如果都是强引用则导致互相持有无法正确的释放,造成循环引用。

weak和assgin的区别

  • 修饰的对象不同:weak修饰OC对象类型的数据,assgin修饰的基本数据变量。
  • 引用计数:二者都不会增加引用计数。
  • 释放后的结果不同:weak修饰的对象释放之后指针自动为nil避免访问野指针crash,assgin修饰的对象释放之后指针仍然存在,成为野指针
  • 修饰delegate:MRC下assgin, ARC下weak,二者都是为了避免循环引用。

2.3 strong

strong是最常用的修饰符,主要用来修饰OC对象类型的数据:(NSNumber,NSString,NSArray、NSDate、NSDictionary、模型类等)。 strong是强引用,在ARC下等于retain,这一点区别于weak。

strong是我们通常所说的指针拷贝(浅拷贝),内存地址保持不变,只是产生了一个新的指针,新指针和引用对象的指针指向同一个内存地址,没有生成新的对象,多了一个指向该对象的指针。

⚠️:由于使用的是一个内存地址,当该内存地址存储的内容发生变更的时候导致属性也跟着变更。

2.4 copy关键字

同样用于修饰OC对象类型的数据,同时在MRC时期用来修饰block,因为MRC时期block要从栈区copy到堆区。现在的ARC系统自动给我们做了这个操作。也就是现在使用strong或者copy修饰block都可以。

copy和strong相同点在于都是属于强引用,引用计数 + 1,但是copy修饰的对象是内存拷贝,在引用的时候会生成一个新的内存地址和指针,和引用对象完全同,也不会因为引用属性的变更而改变。

copy关键字和strong的区别

copy:内存拷贝-深拷贝,内存地址不同,指针地址也不同。
storng: 指针拷贝-浅拷贝,内存地址不变,指针地址不同。

声明两个copy属性,两个strong属性,分别为可变和不可变类型:

@property(nonatomic,strong)NSString * Strstrong;
@property(nonatomic,copy)NSString * Strcopy;
@property(nonatomic,copy)NSMutableString * MutableStrcopy;
@property(nonatomic,strong)NSMutableString * MutableStrstrong;

1. 不可变对象对属性进行赋值,查看strong修饰和copy修饰的区别

// 不可变对象对属性进行赋值,查看strong修饰和copy修饰的区别
- (void)testNormal {
    NSString * OriginalStr = @"我已经开始测试了";
    //对 不可变对象赋值 无论是 strong 还是 copy 都是原地址不变,内存地址都为(0x10c6d75c0),生成一个新指针指向对象(浅拷贝)
    self.Strcopy = OriginalStr;
    self.Strstrong = OriginalStr;
    self.MutableStrcopy = OriginalStr;
    self.MutableStrstrong = OriginalStr;
    // 内容
    NSLog(@"原字符串=>%@\n normal:copy=>%@=====strong=>%@\nMutable:copy=>%@=====strong=>%@",OriginalStr,_Strcopy,_Strstrong,_MutableStrcopy,_MutableStrstrong);
    // 内存地址
    NSLog(@"原字符串=>%p\n normal:copy=>%p=====strong=>%p\nMutable:copy=>%p=====strong=>%p",OriginalStr,_Strcopy,_Strstrong,_MutableStrcopy,_MutableStrstrong);
    // 指针地址
    NSLog(@"原字符串=>%p\n normal:copy=>%p=====strong=>%p\nMutable:copy=>%p=====strong=>%p",&OriginalStr,&_Strcopy,&_Strstrong,&_MutableStrcopy,&_MutableStrstrong);
}

}

请添加图片描述

由上面可以看出,strong修饰的对象,在引用一个对象的时候,内存地址都是一样的,只有指针地址不同,copy修饰的对象也是如此。
为什么呢?不是说copy修饰的对象是生成一个新的内存地址嘛?这里为什么内存地址还是原来的呢?

因为,用不可变对象对属性进行赋值,无论是strong还是copy,都是一样的,原内存地址不变,生成了新的指针地址。

2. 可变对象对属性进行赋值,查看strong和copy的区别

// 可变对象对属性进行赋值,查看strong和copy的区别
- (void)testMutable {
    NSMutableString * OriginalMutableStr = [NSMutableString stringWithFormat:@"我已经开始测试了"];
    //对 不可变对象赋值 无论是 strong 还是 copy 都是原地址不变,内存地址都为(0x10c6d75c0),生成一个新指针指向对象(浅拷贝)
    self.Strcopy = OriginalMutableStr;
    self.Strstrong = OriginalMutableStr;
    self.MutableStrcopy = OriginalMutableStr;
    self.MutableStrstrong = OriginalMutableStr;
    [OriginalMutableStr appendFormat:@"改变了"];
    // 内容
    NSLog(@"原字符串=>%@\n normal:copy=>%@=====strong=>%@\nMutable:copy=>%@=====strong=>%@",OriginalMutableStr,_Strcopy,_Strstrong,_MutableStrcopy,_MutableStrstrong);
    // 内存地址
    NSLog(@"原字符串=>%p\n normal:copy=>%p=====strong=>%p\nMutable:copy=>%p=====strong=>%p",OriginalMutableStr,_Strcopy,_Strstrong,_MutableStrcopy,_MutableStrstrong);
    // 指针地址
    NSLog(@"原字符串=>%p\n normal:copy=>%p=====strong=>%p\nMutable:copy=>%p=====strong=>%p",&OriginalMutableStr,&_Strcopy,&_Strstrong,&_MutableStrcopy,&_MutableStrstrong);

请添加图片描述

在上面的结果可以看出,strong修饰的属性内存地址依然没有改变,但是copy修饰的属性内存值产生了变化。
由此得出结论:
对可变对象赋值 strong 是原地址不变,引用计数+1(浅拷贝)。 copy是生成一个新的地址和对象生成一个新指针指向新的内存地址(深拷贝)

3. 此时改变OriginalMutableStr的值
请添加图片描述
结果:

请添加图片描述

结果:

  1. 是strong 修饰的属性,跟着进行了改变
  2. 当改变了原有值的时候,由于OriginalMutableStr是可变类型,是在原有内存地址上进行修改,无论是指针地址和内存地址都没有b改变,只是当前内存地址所存放的数据进行改变。由于 strong 修饰的属性虽然指针地址不同,但是指针是指向原内存地址的,所以会跟着 OriginalMutableStr 的改变而改变。
  3. ⚠️ 不同于strong,copy修饰的类型不仅指针地址不同,而且指向的内存地址也和OriginalMutableStr 不一样,所以不会跟着 OriginalMutableStr 的改变而改变。

注意

  1. 使用self.Strcopy 和 _Strcopy 来赋值也是两个不一样的结果,因为后者没有调用 set 方法,而 copy 和 strong 之所以会产生差别就是因为在 set 方法中,copy修饰的属性: 调用了 _Strcopy = [Strcopy copy] 方法

2.5 多种copy模式:copy 和 mutableCopy 对 容器对象 进行操作

在对容器对象(NSArray)进行copy操作时,分为多种:

  1. copy:仅仅进行了指针拷贝
  2. mutableCopy:进行内容拷贝,这里的单层指的是完成了NSArray对象的深copy,而未对其容器内对象进行处理使用(NSArray对象的内存地址不同,但是内部元素的内存地址不变)

2.6 问题总结

  1. 浅拷贝和深拷贝的区别?
  • 浅拷贝只是对 内存地址的复制,两个指针指向同一个地址,增加被拷贝对象的引用计数,没有发生新的内存分配。
    深拷贝:目标对象指针和源对象指针,指向两片内容相同的内存空间。
    2个特点:不会增加被拷贝对象的引用计数,产生了新内存分配,出现了2块内存。
  • 总结区别:
    • 浅拷贝增加引用计数,不产生新的内存。
    • 深拷贝不增加引用计数,会新分配内存
  1. copy关键字影响了对象的可变和不可变属性吗?
  • 可变对象(mutable)copy和mutableCopy都是深拷贝
  • 不可变对象(immutable)的copy是浅拷贝,mutableCopy是深拷贝
  • copy方法返回的都是不可变对象,若被拷贝对象是可变对象,返回的也是不可变对象。
    在这里插入图片描述
  1. NSMutableArray用copy修饰会出现什么问题?

出现调用可变方法不可控问题,会导致程序崩溃。给Mutable 被声明为copy修饰的属性赋值, 过程描述如下:

  • 如果赋值过来的是NSMutableArray对象,会对可变对象进行copy操作,拷贝结果是不可变的,那么copy后就是NSArray
  • 如果赋值过来的是NSArray对象, 会对不可变对象进行copy操作,拷贝结果仍是不可变的,那么copy之后仍是NSArray。
  • 所以不论赋值过来的是什么对象,只要对NSMutableArray进行copy操作,返回的对象都是不可变的。
    那原来属性声明的是NSMutableArray,可能会调用了add或者remove方法,拷贝后的结果是不可变对象,所以一旦调用这些方法就会程序崩溃(crash)
  1. 那你说说strong和weak的区别是?
  • strong 表示指向并拥有该对象。**其修饰的对象引用计数会加1.**该对象只要引用计数不为0则不会被销毁。当然强制将其置为nil也可以销毁它。
  • weak 表示指向但不拥有该对象。其修饰的对象引用计数不会增加无需手动设置,该对象会自行在内存中销毁。
  1. 如何理解的atomic的线程安全呢,有没有什么隐患?
  • 保证setter和getter存取方法的线程安全(仅仅对setter和getter方法加锁)。
  • atomic对一个数组,进行赋值或获取,是可以保证线程安全的。但是如果进行数组进行操作,比如给数据加对象或移除对象,是不在atomic的保证范围。
  1. weak属性修饰的变量,如何实现在变量没有强引用后自动置为 nil ?
  • runtime 维护了一个weak_table_t 弱引用表 ,用于存储指向某一个对象的所有weak指针。weak表其实是一个哈希表,
    key是所指对象的地址,value是weak指针的地址的数组。
  • 在对象回收的时候,根据对象的地址将所有weak指针地址的数组,遍历数组把其中的数据置为nil

3. 线程安全的关键字 (nonatomic, atomic)

3.1 nonatomic关键字

nonatomic:非原子操作,不加锁,线程执行快,但是多个线程访问同一个属性可能产生crash

3.2 atomic关键字

atomic原子操作:加锁,保证setter和getter存取方法的线程安全(仅仅对setter和getter方法加锁)。因为线程加锁,别的线程访问当前属性的时候会先执行完属性当前的操作。

⚠️注意:atomic只针对属性的 getter/setter 方法进行加锁,所以安全只是针对getter/setter方法来说,并不是整个线程安全,因为一个属性并不只有 setter/getter 方法,例:(如果一个线程正在getter 或者 setter时,有另外一个线程同时对该属性进行release操作,如果release先完成,会造成crash)

4. 修饰变量的关键字 (const,static,extern)

4.1 常量const

常量修饰符,表示不可变,可以用来修饰右边的基本变量和指针变量(放在谁的前面修饰谁(基本数据变量p,指针变量*p))。

  1. const 类型 * 变量名a:可以改变指针的指向,不能改变指针指向的内容。 const放在 * 号的前面约束参数,表示*a只读。只能修改地址a,不能通过a修改访问的内存空间
int x = 12;
int new_x = 21;
const int *px = &x; 
px = &new_x; // 改变指针px的指向,使其指向变量y

  1. 类型 * const 变量名:可以改变指针指向的内容,不能改变指针的指向。 const放后面约束参数,表示a只读,不能修改a的地址,只能修改a访问的值,不能修改参数的地址
复制代码int y = 12;
int new_y = 21;
int * const py = &y;
(*py) = new_y; // 改变px指向的变量x的值

4.2 常量修饰符(const)和宏定义(define)的区别:

使用宏和常量所占用的内存差别不大,宏定义的是常量,常量都放在常量区,只会生成一份内存。
缺点:

  • 编译时刻:宏是预编译(编译之前处理),const是编译阶段 。导致使用宏定义过多的话,随着工程越来越大,编译速度会越来越慢
    宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误。

优点:

  • 宏能定义一些函数,方法。 const不能。

4.3 static

定义所修饰的对象只能在当前文件访问,不能通过extern来引用

默认情况下的全局变量 作用域是整个程序(可以通过extern来引用) 被static修饰后仅限于当前文件来引用 其他文件不能通过extern来引用

  1. static修饰全局变量:只能在本文件中访问,修改全局变量的作用域,生命周期不会改。避免重复定义全局变量(单例模式)
  2. static修饰局部变量:
  • 有时希望函数中的局部变量的值在函数调用结束后不消失而继续保留原值,即其占用的存储单元不释放,在下一次再调用的时候该变量已经有值。这时就应该指定该局部变量为静态变量,用关键字 static 进行声明。
  • 延长局部变量的生命周期(没有改变变量的作用域,只在当前作用域有用),程序结束才会销毁。

⚠️注意:当在对象A里这么写static int i = 10; 当A销毁掉之后 这个i还存在
当再次alloc init一个A的对象之后 在新对象里 依然可以拿到i = 90
除非杀死程序 再次进入才能得到i = 0。

局部变量只会生成一份内存,只会初始化一次。把它分配在静态存储区,该变量在整个程序执行期间不释放,其所分配的空间始终存在

static修饰局部变量还可以延长变脸的生命周期。

- (void)test{
    // static修饰局部变量1
    static int age = 0;
    age++;
    NSLog(@"%d",age);
}
-(void)test2{
    // static修饰局部变量2
    static int age = 0;
    age++;
    NSLog(@"%d",age);
}

[self test];
[self test2];
[self test];
[self test2];
[self test];
[self test2];

打印 1 1 2 2 3 3

由此可见 变量生命周期延长了,作用域没有变

4.4 常量extern

只是用来获取全局变量(包括全局静态变量)的值,不能用于定义变量。

查找优先级: 先在当前文件查找有没有全局变量,没有找到,才会去其他文件查找。

#import "JMProxy.h"
@implementation JMProxy
int ageJMProxy = 20;
@end

@implementation TableViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    extern int ageJMProxy;
    NSLog(@"%d",ageJMProxy);
}
@end

⚠️ extern不能用于定义变量。

4.4 static与const联合使用

声明一个静态的全局只读常量。开发中声明的全局变量,有些不希望外界改动,只允许读取。

iOS中staic和const常用使用场景,是用来代替宏,把一个经常使用的字符串常量,定义成静态全局只读变量.

// 开发中经常拿到key修改值,因此用const修饰key,表示key只读,不允许修改。
static  NSString * const key = @"name";

// 如果 const修饰 *key1,表示*key1只读,key1还是能改变。

static  NSString const *key1 = @"name";

4.5 extern与const联合使用

在多个文件中经常使用的同一个字符串常量,可以使用extern与const组合

extern与const组合:只需要定义一份全局变量,多个文件共享

@interface Person : NSObject
extern NSString * const nameKey = @"name"; 
@end

#import "ViewController.h"
@interface ViewController ()

@end
NSString * const nameKey; // 必须用xonst才能访问到 extern与const组合组合修饰的全局变量

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

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

相关文章

SpringBoot仅会SSM强撸项目--【JSB项目实战】

SpringBoot系列文章目录 SpringBoot知识范围-学习步骤【JSB系列之000】 文章目录 SpringBoot系列文章目录SpringBoot技术很多很多面对越来越紧的时间,越来越少的知识我要怎么办项目里可能要用到的技术前后端分离json其它的必要知识 环境及工具:上代码Co…

Java-IDEA好用的插件

Lombok,结合一些列注解,帮我们轻松解决重复编写实体类get、set、toString、build、构造方法等麻烦 Chinesepinyin-CodeComp,让界面汉化,使用起来更有亲和力 MyBatisX,点击小鸟图标,轻松再Mapper接口与xml文件之间实…

Spring Security OAuth2.0 - 学习笔记

一、OAuth基本概念 1、什么是OAuth2.0 OAuth2.0是一个开放标准,允许用户授权第三方应用程序访问他们存储在另外的服务提供者上的信息,而不需要将用户和密码提供给第三方应用或分享数据的所有内容。 2、四种认证方式 1)授权码模式 2&#x…

云性能监控的应用是提升云服务质量的关键

随着云计算的普及和企业对云服务的广泛应用,保证云服务的质量和性能已成为企业的重要课题。在这一背景下,云性能监控应运而生,成为提升云服务质量的重要工具,也可以说云性能监控的应用是提升云服务质量的关键。 首先,云…

win10 hadoop报错 unable to load native-hadoop library

win10 安装hadoop执行hdfs -namenode format 和运行hadoop的start-all报错 unable to load native-hadoop library 验证: hadoop checknative -a 这个命令返回都是false是错的 返回下图是正确的 winutils: true D:\soft\hadoop-3.0.0\bin\winutils.exe Native li…

LLM-Blender:大语言模型也可以进行集成学习

最近在看arxiv的时候发现了一个有意思的框架:LLM-Blender,它可以使用Ensemble 的方法来对大语言模型进行集成。 官方介绍如下:LLM-Blender是一个集成框架,可以通过利用多个开源大型语言模型(llm)的不同优势来获得始终如一的卓越性…

pycharm 添加pyuic 插件

添加pyuic插件,就能将ui类型的文件转换称py格式的文件 进入主界面 打开文件->设置->外部工具 点击加号,添加扩展 图1 添加外部扩展 名字记作 pyuic,分组到External Tools 描述为ui to py ,地址选择为 python.exe 的目录地址 参数填写&a…

Python实现抽象工厂模式

抽象工厂模式是一种创建型设计模式,用于创建一系列相关或依赖对象的家族,而无需指定具体类。在Python中,可以通过类和接口的组合来实现抽象工厂模式。 下面是一个简单的Python实现抽象工厂模式的示例: # 抽象产品接口 class Abs…

客户案例 | 思腾合力服务器助力西安电子科技大学人工智能实验室建设

客户介绍 西安电子科技大学是以信息与电子学科为主,工、理、管、文多学科协调发展的全国重点大学,直属教育部,是国家“优势学科创新平台”项目和“211工程”项目重点建设高校之一、国家双创示范基地之一、首批35所示范性软件学院、首批9所示范…

微服务系列(1)-who i am?

微服务系列(1)-我是谁 应用架构的演化 简单来说系统架构可以分为以下几个阶段:复杂的臃肿的单体架构-SOA架构-微服务 单体架构及其所面临的问题 在互联网发展初期,用户数量少,流量小,硬件成本高。因此…

LangChain Agents深入剖析及源码解密上(一)

LangChain Agents深入剖析及源码解密上(一) LangChain Agents深入剖析及源码解密上 Agent工作原理详解 本节会结合AutoGPT的案例,讲解LangChain代理(Agent)为核心的内容。我们前面已经谈了代理本身的很多内容,也看了绝大部分的源代码,例如:ReAct的源代码,还有mrkl的源代…

HDFS基本操作命令

这里写目录标题 HDFS Shell CLI客户端说明常用命令hadoop fs -mkdir [-p] <path>hadoop fs -ls [-h] [-R] [<path>...]上传文件到指定目录下方法一:hadoop fs -put [-f] [-p] <localsrc>.....<dst>方法二&#xff1a;hadoop fs -moveFromLocal <loc…

金融机构如何管理UPS设备?这个方法超值!

UPS监控在金融行业中扮演着至关重要的角色&#xff0c;确保金融机构在电力故障或波动的情况下依然能够保持业务的稳定运行。 因此&#xff0c;UPS监控在金融行业中扮演着守护者的角色&#xff0c;确保金融机构能够在复杂的电力环境中持续稳健地运行。 客户案例 一家国际性的金…

数据库应用:Redis安装部署

目录 一、理论 1.缓存 2.关系型数据库与非关系型数据库 3.Redis 4.Redis安装部署 5.Redis命令工具 6.Redis数据库常用命令 7.Redis多数据库操作 二、实验 1.Redis安装部署 2.Redis命令工具 3.Redis数据库命令 4.Redis多数据库操作 三、问题 1.RESP连接CentOS 7 R…

KnowStreaming系列教程第二篇——项目整体架构分析

一、KS项目代码结构&#xff1a; ks项目代码结构如上&#xff1a; (1)km-console 是前端部分&#xff0c;基于React开发 (2)km-rest 是后端部分&#xff0c;主要是接受前端请求&#xff0c;对应controller相关代码所在模块 (3)km-biz:业务逻辑处理 (4)km-core:核心逻辑 (5…

MapGIS“透明”地下空间,助力城市纵向发展新实践

城市地下空间拓展是城市化发展的必然趋势&#xff0c;城市化发展需要拓展城市空间&#xff0c;城市空间的横向拓展以蔓延式、平面化的发展模式造成土地资源的巨大浪费&#xff0c;地下空间是城市战略性新型国土资源&#xff0c;开发利用城市地下空间是提高土地利用效率、扩大城…

P2096 最佳旅游线路

竖直方向可以随便走嘛&#xff0c;所以求出每一列 的最大值&#xff0c;再做比较就可以了(求最大子段和)。 ACcode: #include<bits/stdc.h> using namespace std; int n,m,a[105][20010],b[20010],dp[20010]; void solve() {cin>>n>>m;for(int i1;i<n;i)f…

linux服务器部署

文章目录 一、基本工具安装1.使用vi命令编辑文件 二、安装1.jdk2.读入数据 总结 一、基本工具安装 1.使用vi命令编辑文件 注:如果vi命令没有&#xff0c;可以使用yum -y install vim或者apt-get install vim命令安装。 Linux操作系统第二讲 二、安装 1.jdk 参考 卸载jdk…

Spring MVC 是什么?

一、什么是 Spring MVC&#xff1f; 官方对于 Spring MVC 的描述是这样的&#xff1a; Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web …

Robust Unsupervised StyleGAN Image Restoration总结整理

鲁棒的无监督StyleGAN图像恢复 一.创新点 现有的无监督方法必须针对每个任务和降级级别进行仔细调整。&#xff08;这里每个任务都是什么&#xff1f;降级级别是什么&#xff1f;&#xff09; 在这里使用StyleGAN图像恢复健壮&#xff0c;即单组超参数在宽范围的退化水平上起…