当前位置: 首页 > news >正文

【iOS】属性关键字

文章目录

  • 前言
  • 一、深拷贝与浅拷贝
    • 1、OC的拷贝方式有哪些
    • 2. OC对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝?
    • 3. 自定义对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝?
    • 4. 判断当前的深拷贝的类型?(区别是单层深拷贝还是完全深拷贝),2种深拷贝类型的相互转换?
    • 5. 代码如下所示:既然对象的mutableCopy是深拷贝,那为什么更改dataArray2,dataArray3也发生了改变?如何解决这个问题?
    • 6. 代码如下图所示:initWithArray:copyItems:YES 仅仅能进行一层深拷贝,对于第二层或者更多层的就无效了。如果想要对象每层都是深拷贝,该怎么做?
  • 二、属性关键字
    • @property
    • @synthesize
    • @dynamic
    • 原子操作
      • atomic
      • nonatomic
    • 读写权限
      • readwrite
      • readonly
    • 内存管理
      • weak
      • assign
      • assign和weak的比较
      • retain
      • strong
      • copy
      • strong与copy的区别
      • strong和copy关键字的用法
    • 1. 常用的基本类型对应Foundation数据类型?
    • 2. 定义属性的格式?(定义属性时修饰符的顺序?)
    • 3. ARC下@property的默认属性?
    • 4. 属性的读写权限关键字的含义?
    • 5. 属性的原子操作关键字的含义?
    • 6、delegate应该使用哪种关键字修饰?
    • 7、以下属性的声明有什么问题?如果一定要这么定义,如何修改成正确的呢?
    • 8、为什么@property属性用copy修饰不可变对象,而用strong修饰可变对象呢?
  • 总结


前言

先前已经在iOS的学习过程中学习了一些属性关键词的简单用法,今天这篇博客来更加深入探讨iOS中的属性关键字以及温习深拷贝与浅拷贝


一、深拷贝与浅拷贝

1、OC的拷贝方式有哪些

OC对象(集合类型和非集合类型)有2种拷贝方式,分别为浅拷贝和深拷贝。

在这里插入图片描述

  • 浅拷贝:指针拷贝,即源对象和副本对象的指针指向了同一个区域
  • 深拷贝:内容拷贝,即源对象和副本对象的指针分别指向不同的两块区域

深拷贝包括了单层深拷贝和完全深拷贝。具体后面会有讲到

  • 单层深拷贝:对于副本对象本身这一层是深拷贝,它里面的所有对象都是浅拷贝。
  • 完全深拷贝:对于副本对象本身以及它里面的所有对象都是深拷贝。

浅拷贝和深拷贝的区别:

  • 是否开辟了新的内存空间
  • 是否影响了引用计数

2. OC对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝?

  • 可变对象(集合类型/非集合类型)的copy和mutableCopy都是深拷贝。
  • 不可变对象(集合类型/非集合类型)的copy是浅拷贝,mutableCopy是深拷贝。
  • copy方法返回的都是不可变对象。
    在这里插入图片描述
    具体实现过程在之前博客中已经给出:【iOS】深拷贝与浅拷贝

3. 自定义对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝?

  • 自定义对象实现copy和mutableCopy都为深拷贝

因为我们的自定义对象没有copyWithZone:和mutableCopyWithZone:两个方法,需要遵守SCopying和NSMutableCopying协议来实现这两个方法,也已经在上面的链接中具体实现


4. 判断当前的深拷贝的类型?(区别是单层深拷贝还是完全深拷贝),2种深拷贝类型的相互转换?

在这里我们讨论的深拷贝的类型都是对于容器类对象来讲的,因为对于非容器类对象我们并没有单层深拷贝与完全深拷贝的区分

  • 单层深拷贝:对于副本对象本身是深拷贝,但是容器中的所有对象都是浅拷贝
  • 完全深拷贝:对于副本对象本身与其里面的所有对象都是深拷贝

实现方式

  • 单层深拷贝:
    当容器类对象中的对象是容器类对象时,使用initWithArray:copyItems:方法(第二个参数设置为YES)可以实现单层浅拷贝,因为这种拷贝只能产生一层深拷贝,对于第二层或者更多层的就无效了
  • 完全深拷贝:
    1、归档和解档来实现完全深拷贝。
    举例:dataArray3 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:dataArray2]]
    2、当容器类对象中的对象是自定义对象或不为immutable对象,使用initWithArray:copyItems:方法(第二个参数设置为YES)可以实现完全深拷贝
    这里其实也很好理解,因为我们实现自定义对象的copy时我们是需要自己实现我们的copyWithZone:和mutableCopyWithZone:两个方法,而自定义对象的拷贝始终都是深拷贝,因此通过这个方法可以实现完全深拷贝

5. 代码如下所示:既然对象的mutableCopy是深拷贝,那为什么更改dataArray2,dataArray3也发生了改变?如何解决这个问题?

在这里插入图片描述
在这里插入图片描述

问题(1). 对象的mutableCopy是深拷贝,那为什么更改dataArray2,dataArray3也发生了改变?

dataArray3 = [dataArray2 mutableCopy];  
这段代码实现的是单层深拷贝,dataArray3是dataArray2深拷贝得到的数组,但是对于array2数组中的对象array仅仅对其进行了浅拷贝,因此更改array2时array3随之改变

问题(2). 如何解决这个问题?

因为更改的对象非immutable对象,所以使用initWithArray:copyItems:方法可以实现完全深拷贝可以轻松解决这个问题

        dataArray3 = [[NSMutableArray alloc] initWithArray:dataArray2 copyItems:YES];

在这里插入图片描述

6. 代码如下图所示:initWithArray:copyItems:YES 仅仅能进行一层深拷贝,对于第二层或者更多层的就无效了。如果想要对象每层都是深拷贝,该怎么做?

在这里插入图片描述
在这里插入图片描述
使用initWithArray:copyItems:实现深拷贝仅能产生一层深拷贝,再多就没有办法实现深拷贝了,所以就需要用到解档与归档

dataArray3 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:dataArray2]];

二、属性关键字

@property

属性用于封装对象中的数据,属性的本质是 ivar + setter + getter。
可以用 @property 语法来声明属性。@property 会帮我们自动生成属性的 setter 和 getter 方法的声明

@synthesize

帮我们自动生成 setter 和 getter 方法的实现以及合成实例变量。

@dynamic

告诉编译器不用自动进行 @synthesize,你会在运行时再提供这些方法的实现,无需产生警告,但是它不会影响 @property 生成的 setter 和 getter 方法的声明。

@dynamic ivar;

以前我们需要手动对每个 @property 添加 @synthesize,而在 iOS 6 之后 LLVM 编译器引入了 property autosynthesis,即属性自动合成。换句话说,就是编译器会自动为每个 @property 添加 @synthesize。

但是这样看来似乎@synthesize就没有什么用了,其实不然:

  1. 如果我们重写了setter 和 getter 方法,则编译器就不会自动为这个 @property 添加 @synthesize,所以我们需要手动添加 @synthesize;
  2. 如果该属性是 readonly,那么只要你重写了 getter 方法,property autosynthesis 就不会执行
  3. 另外就是实现协议中要求的属性。当我们在协议中使用@property声明一个属性,在某个类中遵循这个协议,我们就必要在类中使用@synthesize来获取这个属性的成员变量,并且得到其set/get的实现函数
    在这里插入图片描述
    在这里插入图片描述

这里说到了协议,那么我们再来讨论一下Category是否能够添加使用@synthesize来合成成员变量以及实现setget方法呢?

答案是并不行,在我们的Category中我们可以声明属性,但不能声明成员变量

因为分类不是类,我们可以通过@property声明属性,但是并不能声明成员变量

Category是用来扩展现有类的功能的,而不是创建全新的类或修改类的内部实现。因此,Category不允许添加实例变量(成员变量)或使用@synthesize来合成成员变量。

以下笔者将一下为什么Category不能添加实例变量和使用@synthesize的原因:

  1. 我们的实例变量的声明我们通常需要放在类的文件中,以确保封装性,而因为分类不是类,Category只能添加方法和属性的声明,因此不允许添加实例变量,因为这会破坏类的封装性。
  2. 当我们使用@synthesize来合成成员变量时,编译器会自动生成对应的实例变量,由于Category不能添加实例变量,编译器无法为Category中的属性生成对应的实例变量,因此不允许使用@synthesize。

使用 @synthesize 关键字会合成属性的实例变量以及属性的 getter 和 setter 方法

这是我们从代码使用的层面上分析Category不能使用@synthesize的原因,接下来我们从底层结构的层面分析一下原因:

分类是在运行时去把分类中的方法添加到类的方法列表里,类的底层其实是结构体,分类可以添加属性,不能添加成员变量,因为结构体声明后不能加成员变量,而属性是结构体里的一个列表(rw:property_array_t / ro: property_list_t),就是可以加的。

而分类当中不能添加成员变量,只能通过关联对象间接实现分类有成员变量的效果,这个知识点笔者还没能理解,后面学到了加以补充

原子操作

原子操作:属性是否有原子性可以理解为线程是否安全。

  • atomic

原子性,加同步锁,默认修饰符。 使用atomic会损耗性能,也不一定保证线程安全。如果保证线程安全需要使用其他锁机制。

  • nonatomic

非原子性,不实用同步锁。 声明属性时基本设置为nonatomic。使用nonatomic能够提高访问性能。

这里笔者还没学到锁的相关知识,等学到了再加以补充

读写权限

读写权限不写时默认为 readwrite

  • readwrite

属性拥有setter方法和getter方法

  • readonly

仅有getter方法

内存管理

内存管理相关的关键词我们也可以理解为setter相关控制符,setter相关的修饰符表明setter方法应该如何实现

weak

  1. 只能修饰对象类型;
  2. ARC 下才能使用;
  3. 修饰弱引用,不增加对象引用计数,主要可以用于避免循环引用;
  4. weak 修饰的对象在被释放之后,会自动将指针置为 nil,不会产生悬垂指针;
  5. 对于视图,通常还是用在 xib 和 storyboard 上;代码中对于有必要进行 remove 的视图也可以使用 weak,这样 remove 之后会自动置为 nil。

assign

  1. 既可以修饰基本数据类型,也可以修饰对象类型;
  2. setter 方法的实现是直接赋值,一般用于基本数据类型 ;
  3. 修饰基本数据类型,如 NSInteger、BOOL、int、float 等;
  4. 修饰对象类型时,不增加其引用计数;
  5. 会产生悬垂指针(悬垂指针:assign 修饰的对象在被释放之后,指针仍然指向原对象地址,该指针变为悬垂指针。这时候如果继续通过该指针访问原对象的话,就可能导致程序崩溃)。

assign和weak的比较

相同点:对对象的引用计数没有影响,即都是弱引用。
不同点:

  1. 修饰的对象类型不同:

weak只能修饰OC对象(如UIButton、UIView等)。

assign可以修饰基本数据类型(NSInteger、NSUInteger、CGFloat、NSTimeInterval、int、float、BOOL等)和OC对象(如果用assign修饰OC对象,对象销毁时可能会产生悬垂指针,从而出现crash,不要这么做)。

注意: 使用assign修饰OC对象可能会导致程序crash,所以assign最好只用来修饰基本数据类型。

  1. 赋值的方式不同:weak复制引用,assign复制数据。
  2. 对象销毁后的状态不同:weak自动为nil,assign不变。

retain

  1. MRC 下使用,ARC 下基本使用 strong;
  2. 修饰强引用,保留新值,释放旧值,再设置新值,同时将新对象的引用计数加 1;
  3. setter 方法的实现是 release 旧值,retain 新值,用于 OC 对象类型。

我们这里详细解释一下保留新值,释放旧值,再设置新值:


strong

  1. ARC 下才能使用;
  2. 原理同 retain;
  3. 但是在修饰 block 时,strong 相当于 copy,而 retain 相当于 assign。

copy

setter 方法的实现是 release 旧值,copy 新值
一般用于 block、NSString、NSArray、NSDictionary 等类型。使用 copy 或 strong 修饰 block 其实都一样,用 copy 是为了和 MRC 下保持一致的写法;用于 NSString、NSArray、NSDictionary 是为了保证赋值后是一个不可变对象,以免遭外部修改而导致不可预期的结果。


当你使用copy特性来声明一个属性时,这意味着当设置属性时,属性会复制传入的对象,而不仅仅是保持对传入对象的引用。这通常用于确保属性拥有自己的独立副本,以避免不经意的更改。

下面是一个示例:

@property (nonatomic, copy) NSString *name;

在上面的代码中,name属性使用了copy特性。当你设置name属性时,传入的字符串会被复制,而不是保留对原始字符串的引用。这意味着如果原始字符串在后续被修改,name属性的值不会受到影响

合成的存取方法会自动处理这个行为。例如,如果你设置self.name = @“Alice”,name属性会将传入的字符串@"Alice"复制为自己的副本,而不仅仅是保存对@"Alice"的引用。


strong与copy的区别

如果属性声明中指定了copy特性,合成方法会使用类的copy方法,这里注意:属性并没有mutableCopy特性。即使是可变的实例变量,也是使用copy特性,正如方法 copyWithZone:的执行结果。所以,按照约定会生成一个对象的不可变副本

  • 相同之处:用于修饰标识拥有关系的对象
  • 不同之处:strong的赋值是多个指针指向同一个地址,而copy的复制就是每次会在内存中复制一份对象,指针指向不同的地址。
    所有对于不可变对象我们应该使用copy修饰,为确保对象中的字符串值不会无意变动,应该在设置新属性时拷贝一份

再通俗一点的理解就是用strong修饰属性并对其进行赋值时可以理解为指针拷贝,而用copy修饰时可以理解为内容拷贝

我们来给出代码的例子:

  • strong修饰
NSMutableString *otherName = [[NSMutableString alloc] initWithString:@"Jack"];
Person *person = [[Person alloc] init];
person.name = otherName;
person.age = 23;[otherName appendString:@" and Mary"];
NSLog(@"person.name = %@",person.name);
NSLog(@"%p, %p", person.name, otherName);

我们分别打印属性以及属性的地址与原始对象的地址:
在这里插入图片描述
可以看到我们的属性是可以被修改的,并且属性的地址与原始对象的地址相同,说明用strong修饰属性时,属性指向原对象的内存地址,同时使该对象引用计数加1

  • copy修饰
NSMutableString *otherName = [[NSMutableString alloc] initWithString:@"Jack"];
Person *person = [[Person alloc] init];
person.name = otherName;
person.age = 23;[otherName appendString:@" and Mary"];
NSLog(@"person.name = %@",person.name);
NSLog(@"person.name = %@",otherName);
NSLog(@"%p, %p", person.name, otherName);

当我们属性修饰改为copy时,打印出的结果如下图所示:
在这里插入图片描述

可以看到我们属性指向的地址与原始对象的地址不同,并且我们的属性指向的内存空间的数据并未被修改,但是原对象的数据被修改了,说明用copy进行修饰属性对其进行赋值时会创建一块新的内存空间,属性指向新创建的内存空间,本质其实就是内容拷贝(深拷贝)

strong和copy关键字的用法

@property属性用copy修饰不可变对象,用strong修饰可变对象。

1. 常用的基本类型对应Foundation数据类型?

在声明一个属性时,尽量使用Foundation框架的数据类型,使代码的数据类型更统一。
基本类型和Foundation数据类型的对应关系如下:

  • int -> NSInteger
  • unsigned -> NSUInteger
  • float -> CGFloat
  • 动画时间 -> NSTimeInterval

2. 定义属性的格式?(定义属性时修饰符的顺序?)

推荐按照下面的格式来定义属性

@property (nonatomic, readwrite, copy) NSString *name;

属性的修饰符应该按照上面的顺序排列:原子操作、读写权限、内存管理

3. ARC下@property的默认属性?

对于基本数据类型:atomic、readwrite、assign
对于普通的Objective-C对象:atomic、readwrite、strong

4. 属性的读写权限关键字的含义?

读写权限

readwrite:可读可写,默认修饰符。会自动生成getter和setter
readonly:只读。只会生成getter而不生成setter

5. 属性的原子操作关键字的含义?

原子操作:属性是否有原子性可以理解为线程是否安全。

atomic:原子性,加同步锁,默认修饰符。
使用atomic会损耗性能,也不一定保证线程安全。如果保证线程安全需要使用其他锁机制。

nonatomic:非原子性,不实用同步锁。
声明属性时基本设置为nonatomic。使用nonatomic能够提高访问性能。

这里设计到了一些锁的知识,还没有学到,后面学到再进行补充

6、delegate应该使用哪种关键字修饰?

MRC时期:使用assign,这样不会造成循环引用,但是需要手动释放。
ARC时期:最好使用weak,如果使⽤了assign需要⼿动释放。如果没写释放逻辑,当⻚面销毁的时候,很可能出现delegate对象无效,导致程序crash。

7、以下属性的声明有什么问题?如果一定要这么定义,如何修改成正确的呢?

@property (nonatomic, copy) NSMutableArray *mutableArray;

如果我们对属性进行增删改等操作时,程序会崩溃,因为当我们对属性进行赋值时,copy复制的是一个不可变的NSArray对象

具体分析:不应该使用copy关键字来修饰可变对象。
copy修饰的属性会在内存里拷贝一份对象,即两个指针指向不同的内存地址。
Foundation框架提供的可变对象类型都已实现了NSCopying协议,所以使用copy方法返回的都是不可变对象
本题中,用copy关键字修饰了可变数组,那么当对该属性赋值时会得到一个NSArray类型的不可变数组。
因为是NSArray类型,即是不可变的数组类型,所以如果对属性进⾏了可变数组的增删改功能都会导致crash。

所以正确写法如下
@property (nonatomic, strong) NSMutableArray *mutableArray;


如果一定要用copy,我们需要重写setter方法,因为对当属性用copy修饰进行赋值时,会自动调用setter方法执行copyWithZone:方法,返回的是不可变对象,所以我们需要对待吗进行如下修改:

// .h文件
@property (nonatomic, copy) NSMutableArray *mutableArray;// .m文件
// 重写setter⽅法 使_mutableArray变为可变的copy
- (void)setMutableArray:(NSMutableArray *)mutableArray {_mutableArray = [mutableArray mutableCopy]; 
}- (void )viewDidLoad { [super viewDidLoad];NSMutableArray *array = [NSMutableArray arrayWithObjects:@1, @2, nil]; self.mutableArray = array;[self.mutableArray removeObjectAtIndex:0]; NSLog(@"self.mutableArray:%@", self.mutableArray);
}输出:
self.mutableArray:( 2
)

8、为什么@property属性用copy修饰不可变对象,而用strong修饰可变对象呢?

  • 用copy修饰不可变对象:

copy修饰的属性会在内存里拷贝一份对象,即两个指针指向不同的内存地址。
Foundation框架提供的对象类型都已实现了NSCopying协议,所以使用copy方法返回的都是不可变对象。
即使源对象是可变对象(实现属性所用的对象是mutable),copy后的对象也不会随之改变
确保了对象不会无意间被改动。

  • 用strong修饰可变对象:

strong修饰的属性是对属性进行了强引用,即两个指针会指向同一个内存地址。
如果源对象可变,strong修饰的对象也会随之改变。

总结

属性关键字的细节还有很多很多,后面学到更多的知识会加以补充

相关文章:

【iOS】属性关键字

文章目录 前言一、深拷贝与浅拷贝1、OC的拷贝方式有哪些2. OC对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝?3. 自定义对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝?4. 判断当前的深拷贝的类型?(区别是单层深拷贝还是完全深拷贝…...

【计算机基础】Git从安装到使用,详细每一步!扩展Github\Gitlab

📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨ 📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸对你有帮助,可点赞 👍…...

深入了解Docker镜像操作

Docker是一种流行的容器化平台,它允许开发者将应用程序及其依赖项打包成容器,以便在不同环境中轻松部署和运行。在Docker中,镜像是构建容器的基础,有些家人们可能在服务器上对docker镜像的操作命令不是很熟悉,本文将深…...

嵌入式开发-单片机学习介绍

一、单片机入门篇 单片机的定义和历史 单片机是一种集成了微处理器、存储器、输入输出接口和其他功能于一体的微型计算机,具有高度的集成性和便携性。单片机的历史可以追溯到20世纪70年代,随着微电子技术的不断发展,单片机逐渐成为了工业控…...

5、Spring之Bean生命周期源码解析(销毁)

Bean的销毁过程 Bean销毁是发送在Spring容器关闭过程中的。 在Spring容器关闭时,比如: AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = (UserService) context.getBean("userSe…...

开发多点触控MFC应用程序

当下计算机变得越来越智能化,越来越无所不能,触摸屏的普及只是时间问题了。 虽然鼠标和键盘不会很快就离开人们的视野,毕竟人们使用鼠标跟键盘已经成为一种习惯,但是处理信息或者说操作计算机的其他方法也层出不穷——比如触控技术…...

使用nlohmann json库进行序列化与反序列化

nlohmann源码仓库&#xff1a;https://github.com/nlohmann/json使用方式&#xff1a;将其nlohmann文件夹加入&#xff0c;包含其头文件json.hpp即可demo #include <iostream> #include "nlohmann/json.hpp" #include <vector>using json nlohmann::js…...

高教社杯数模竞赛特辑论文篇-2012年A题:葡萄酒的评价(附获奖论文)

目录 摘 要 一、问题重述 二、问题分析 2.1 问题一的分析 2.2 问题二的分析...

手写RPC——数据序列化工具protobuf

手写RPC——数据序列化工具protobuf Protocol Buffers&#xff08;protobuf&#xff09;是一种用于结构化数据序列化的开源库和协议。下面是 protobuf 的一些优点和缺点&#xff1a; 优点&#xff1a; 高效的序列化和反序列化&#xff1a;protobuf 使用二进制编码&#xff0c…...

【MATLAB第70期】基于MATLAB的LightGbm(LGBM)梯度增强决策树多输入单输出回归预测及多分类预测模型(全网首发)

【MATLAB第70期】基于MATLAB的LightGbm(LGBM)梯度增强决策树多输入单输出回归预测及多分类预测模型&#xff08;全网首发&#xff09; 一、学习资料 (LGBM)是一种基于梯度增强决策树(GBDT)算法。 本次研究三个内容&#xff0c;分别是回归预测&#xff0c;二分类预测和多分类预…...

Linux进程间通信的几种方式

分析&回答 管道&#xff08;pipe&#xff09;以及有名管道&#xff1a;管道可用于有亲缘关系进程间通信&#xff0c;有名管道克服了管道没有名字的限制&#xff0c;因此具有管道的所有功能之外&#xff0c;它还允许无亲缘关系进程间通信。信号&#xff08;Signal&#xff…...

Android 13.0 Launcher3定制之双层改单层(去掉抽屉式一)

1.概述 在13.0的系统产品开发中,对于在Launcher3中的抽屉模式也就是双层模式,在系统原生的Launcher3中就是双层抽屉模式的, 但是在通过抽屉上滑的模式拉出app列表页,但是在一些产品开发中,对于单层模式的Launcher3的产品模式也是常用的功能, 所以需要了解抽屉模式,然后修…...

【uniapp 配置启动页面隐私弹窗】

为什么需要配置 原因 根据工业和信息化部关于开展APP侵害用户权益专项整治要求&#xff0c;App提交到应用市场必须满足以下条件&#xff1a; 1.应用启动运行时需弹出隐私政策协议&#xff0c;说明应用采集用户数据 2.应用不能强制要求用户授予权限&#xff0c;即不能“不给权…...

2分钟讲清楚C#的委托, C语言的函数指针,Java的函数式接口

很多小伙伴学习C# 的委托时往往一头雾水, 不明白委托是什么, 有什么作用, 今天我就用2分钟讲清楚 这是一个C# 的控制台程序 定义一个最简单的委托 delegate int Calculate(int a, int b); 这相当于定义了一个Calculate类型, 只不过这个类型需要传入2个int类型的参数 返回值也…...

华为云物联网平台微信小程序开发教程2.0【完整详细教程】

一、简介 在之前曾发布过一篇文章“华为云物联网平台的微信小程序开发”&#xff0c;在最近接到部分用户私信在使用开发过程中出现的问题&#xff0c;例如API访问的"401"现象等问题&#xff0c;在重新查看上面的文章教程时发现教程内容的步骤不详细&#xff0c;现对教…...

Laravel 模型1对1关联 1对多关联 多对多关联 ⑩①

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; THINK PHP &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f44…...

【分类】分类性能评价

评价指标 1、准确率、召回率、精确率、F-度量、ROC ​ 属于各类的样本的并不是均一分布&#xff0c;甚至其出现概率相差很多个数量级&#xff0c;这种分类问题称为不平衡类问题。在不平衡类问题中&#xff0c;准确率并没有多大意义&#xff0c;我们需要一些别的指标。 ​ 通…...

M1 Pro 新芯片安装python2 方案汇总

前言&#xff1a;磨刀不误砍柴工&#xff0c;环境装好&#xff0c;才能打工。M1 Pro 新芯片安装python2 文章目录 方案一 docker 容器构造环境&#xff08;如果涉及本地两个仓库需要关联则不适用&#xff09;方案二 使用 pyenv &#x1f680; 作者简介&#xff1a;作为某云服务…...

无涯教程-Android - Broadcast Receivers

Broadcast Receivers 仅响应来自其他应用程序或系统本身的广播消息&#xff0c;这些消息有时称为events或intents。例如&#xff0c;应用程序还可以启动广播&#xff0c;以使其他应用程序知道某些数据已下载到设备并可供他们使用&#xff0c;因此广播接收器将拦截此通信并启动适…...

【Pytorch】Tutorials个人翻译集合

本文记录Pytorch Tutorials文档的翻译文章集合. 由于本人英语能力有限&#xff08;only-CET4&#xff09;&#xff0c;欢迎指正翻译中的错误。 Introduction to PyTorch 【Pytorch】Pytorch文档学习1&#xff1a;Tensors 【Pytorch】Pytorch文档学习2&#xff1a;DATASETS &a…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...

jdbc查询mysql数据库时,出现id顺序错误的情况

我在repository中的查询语句如下所示&#xff0c;即传入一个List<intager>的数据&#xff0c;返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致&#xff0c;会导致返回的id是从小到大排列的&#xff0c;但我不希望这样。 Query("SELECT NEW com…...

TJCTF 2025

还以为是天津的。这个比较容易&#xff0c;虽然绕了点弯&#xff0c;可还是把CP AK了&#xff0c;不过我会的别人也会&#xff0c;还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...