【iOS】ARC内存管理

news2025/1/11 15:10:45

内存管理

  • 内存管理的思考方式
  • iOS底层内存管理方式
    • 1. tagged pointer
    • 2. on-pointer iSA--非指针型iSA
    • 3. SideTables,RefcountMap,weak_table_t
  • 内存管理有关修饰符
    • __strong修饰符
      • 对象的所有者和对象的生命周期
      • `__strong`对象相互赋值
      • 方法参数中使用`__strong`
    • __weak修饰符
      • __weak修饰符的引用计数的问题
    • __unsafe_unretained
    • __autoreleasing修饰符
  • 具体ARC规则:
    • __bridge
    • 属性关键字和所有权修饰符

内存管理的思考方式

怎么说呢。经典再放送咯。

对象操作对应的Objective-C方法
生成并持有对象alloc/new/copy/mutableCopy等方法
持有对象retain方法
释放对象release方法
废弃对象dealloc方法

iOS底层内存管理方式

iOS内存管理方案有三种,我们详细看下每种方案的实现及存在的意义。

1. tagged pointer

标签指针

没有这种管理机制会引起内存浪费,为什么呢?

我们来看下,假设我们要存储一个NSNumber对象,其值是一个整数。正常情况下,如果这个整数只是一个NSInteger的普通变量,那么它所占用的内存是与CPU的位数有关,在32位CPU下占4个字节,在64位CPU下是占8个字节的。而指针类型的大小通常也是与CPU位数相关,一个指针所占用的内存在32位CPU下为4个字节,在64位CPU下也是8个字节。

所以一个普通的iOS程序,如果没有Tagged Pointer对象,从32位机器迁移到64位机器中后,虽然逻辑没有任何变化,但这种NSNumber、NSDate一类的对象所占用的内存会翻倍。如下图所示:

在这里插入图片描述

我们再来看看效率上的问题,为了存储和访问一个NSNumber对象,我们需要在堆上为其分配内存,另外还要维护它的引用计数,管理它的生命期。这些都给程序增加了额外的逻辑,造成运行效率上的损失。

为了改进上面提到的内存占用和效率问题,苹果提出了Tagged Pointer对象。由于NSNumberNSDate一类的变量本身的值需要占用的内存大小常常不需要8个字节,拿整数来说,4个字节所能表示的有符号整数就可以达到20多亿(注:2^31=2147483648,另外1位作为符号位),对于绝大多数情况都是可以处理的。

所以我们可以将一个对象的指针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这是一个特别的指针,不指向任何一个地址。所以,引入了Tagged Pointer对象之后,64位CPU下NSNumber的内存图变成了以下这样:

在这里插入图片描述

当8字节可以承载用于表示的数值时,系统就会以Tagged Pointer的方式生成指针,如果8字节承载不了时,则又用以前的方式来生成普通的指针。以上是关于Tag Pointer的存储细节。
Tagged Pointer的特点:

  • 我们也可以在WWDC2013的《Session 404 Advanced in Objective-C》视频中,看到苹果对于Tagged Pointer特点的介绍:Tagged Pointer专门用来存储小的对象,例如NSNumberNSDate, 当然NSString小于60字节的也可以运用了该手段
  • Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已,因为他没有isa指针。所以,它的内存并不存储在堆中,也不需要mallocfree
  • 在内存读取上有着3倍的效率,创建时比以前快106倍。

由此可见,苹果引入Tagged Pointer,不但减少了64位机器下程序的内存占用,还提高了运行效率。完美地解决了小内存对象在存储和访问效率上的问题。

2. on-pointer iSA–非指针型iSA

在64位系统上只需要32位来储存内存地址,而剩下的32位就可以用来做其他的内存管理

non_pointer iSA 的判断条件:

1 : 包含swift代码;

2:sdk版本低于10.11;

3:runtime读取image时发现这个image包含__objc_rawi sa段;

4:开发者自己添加了OBJC_DISABLE_NONPOINTER_ISA=YES到环境变量中;

5:某些不能使用Non-pointer的类,GCD等;

6:父类关闭。

3. SideTables,RefcountMap,weak_table_t

为了管理所有对象的引用计数和weak指针,苹果创建了一个全局的SideTables,虽然名字后面有个"s"不过他其实是一个全局的Hash表,里面的内容装的都是SideTable结构体而已。它使用对象的内存地址当它的key。管理引用计数和weak指针就靠它了。

因为对象引用计数相关操作应该是原子性的。不然如果多个线程同时去写一个对象的引用计数,那就会造成数据错乱,失去了内存管理的意义。同时又因为内存中对象的数量是非常非常庞大的需要非常频繁的操作SideTables,所以不能对整个Hash表加锁。苹果采用了分离锁技术。

下边是SideTabel的定义:

SideTable
   struct SideTable {
     //锁
     spinlock_t slock;
     //强引用相关
     RefcountMap refcnts;
     //弱引用相关
     weak_table_t weak_table;
     ...
}

当我们通过SideTables[key]来得到SideTable的时候,SideTable的结构如下:

1、一把自旋锁。spinlock_t slock;

自旋锁比较适用于锁使用者保持锁时间比较短的情况。正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文使用,而自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用。

它的作用是在操作引用技术的时候对SideTable加锁,避免数据错误。

苹果在对锁的选择上可以说是精益求精。苹果知道对于引用计数的操作其实是非常快的。所以选择了虽然不是那么高级但是确实效率高的自旋锁。

2、引用计数器 RefcountMap * refcnts;

对象具体的引用计数数量是记录在这里的。

这里注意RefcountMap其实是个C++的Map。为什么Hash以后还需要个Map呢?因为内存中对象的数量实在是太庞大了我们通过第一个Hash表只是过滤了第一次,然后我们还需要再通过这个Map才能精确的定位到我们要找的对象的引用计数器。

引用计数器的数据类型是:

typedef __darwin_size_t size_t;

再进一步看它的定义其实是unsigned long,在32位和64位操作系统中,它分别占用32和64个bit。

苹果经常使用bit mask技术。这里也不例外。拿32位系统为例的话,可以理解成有32个盒子排成一排横着放在你面前。盒子里可以装0或者1两个数字。我们规定最后边的盒子是低位,左边的盒子是高位。

(1UL<<0)的意思是将一个"1"放到最右侧的盒子里,然后将这个"1"向左移动0位(就是原地不动):0b0000 0000 0000 0000 0000 0000 0000 0001

(1UL<<1)的意思是将一个"1"放到最右侧的盒子里,然后将这个"1"向左移动1位:0b0000 0000 0000 0000 0000 0000 0000 0010

下面来分析引用计数器(图中右侧)的结构,从低位到高位。

(1UL<<0)???WEAKLY_REFERENCED

表示是否有弱引用指向这个对象,如果有的话(值为1)在对象释放的时候需要把所有指向它的弱引用都变成nil(相当于其他语言的NULL),避免野指针错误。

(1UL<<1)???DEALLOCATING

表示对象是否正在被释放。1正在释放,0没有

(1UL<<(WORD_BITS-1))???SIDE_TABLE_RC_PINNED

其中WORD_BITS在32位和64位系统的时候分别等于32和64。其实这一位没啥具体意义,就是随着对象的引用计数不断变大。如果这一位都变成1了,就表示引用计数已经最大了不能再增加了。

3、维护weak指针的结构体 weak_table_t * weak_table;

第一层结构体中包含两个元素。

第一个元素weak_entry_t *weak_entries;是一个数组,上面RefcountMap是要通过find(key)来找到精确的元素的。weak_entries则是通过循环遍历来找到对应的entry

(上面管理引用计数器苹果使用的是Map,这里管理weak指针苹果使用的是数组,有兴趣的朋友可以思考一下为什么苹果会分别采用这两种不同的结构)

这个是因为weak的显著的特征来决定的: 当weak对象被销毁的时候,要把所有指向该对象的指针都设为nil。

第二个元素num_entries是用来维护保证数组始终有一个合适的size。比如数组中元素的数量超过3/4的时候将数组的大小乘以2。

第二层weak_entry_t的结构包含3个部分:

1、referent:被指对象的地址。前面循环遍历查找的时候就是判断目标地址是否和他相等。
2、referrers:可变数组,里面保存着所有指向这个对象的弱引用的地址。当这个对象被释放的时候,referrers里的所有指针都会被设置成nil。
3、inline_referrers只有4个元素的数组,默认情况下用它来存储弱引用的指针。当大于4个的时候使用referrers来存储指针。

上面我们介绍了苹果为了更好的内存管理使用的三种不同的内存管理方案,在内部采用了不同的数据结构以达到更高效内存检索。

内存管理有关修饰符

__strong修饰符

__strong修饰符是id类型和对象类型默认的所有权修饰符。

不论调用哪种方法,强引用修饰的变量会持有该对象,如果已经持有则引用计数不会增加。

对象的所有者和对象的生命周期

  1. 持有强引用的变量在超出其作用域时被废弃
  2. 随着强引用的失效,引用的对象会随之释放

__strong对象相互赋值

__strong修饰符的变量不仅只在变量作用域中,在赋值上也能够正确的管理其对象的所有者。

id __strong obj0 = [[NSObject alloc] init];//生成对象A			
id __strong obj1 = [[NSObject alloc] init];//生成对象B		
id __strong obj2 = nil;
obj0 = obj1;//obj0强引用对象B;而对象A不再被ojb0引用,被废弃
obj2 = obj0;//obj2强引用对象B(现在obj0,ojb1,obj2都强引用对象B)	
obj1 = nil;//obj1不再强引用对象B	
obj0 = nil;//obj0不再强引用对象B	
obj2 = nil;//obj2不再强引用对象B,不再有任何强引用引用对象B,对象B被废弃

方法参数中使用__strong

废弃Test对象的同时,Test对象的obj_成员变量也被废除
即成员变量的生存周期是与对象同步的。

__strong导致的循环引用

循环引用就是两个对象相互引用导致在该释放的时候没有释放,从而一直占着内存导致内存泄漏

内存泄漏:在内存该被释放的时候没有释放,导致内存被浪费使用了

举个例子:

#import <Foundation/Foundation.h>

@interface StrongTest : NSObject {
    id __strong _obj;
}
- (void) setObject:(id __strong) obj;

@end

#import "StrongTest.h"
@implementation StrongTest
- (void) setObject:(id)obj {
    _obj = obj;
}
@end

main函数:

#import <Foundation/Foundation.h>
#import "StrongTest.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        StrongTest *testFirst = [[StrongTest alloc] init];//生成Test1
        StrongTest *testSecond = [[StrongTest alloc] init];//生成Test2
        
        [testFirst setObject:testSecond];
        [testSecond setObject:testFirst];
        
        //打印一下引用计数值
        NSLog(@"%lu", CFGetRetainCount((__bridge  CFTypeRef)testFirst));       
    }
    return 0;
}

我们创建了两个对象testFirsttestSecond
每个对象内部都有一个成员变量_obj
其中一个对象的_obj持有另一个对象

最后就会造成下面的结果:

testFirst持有Test1testFirst.obj持有Test2testSecond持有Test2testSecond.obj持有Test1

失效阶段的表示:

what happenTest1引用计数Test1持有者Test2引用计数Test2持有者
初始状态2testFirst, testSecond.obj2testFirst.obj, testSecond
testFirst超出作用域1testSecond.obj2testFirst.obj, testSecond
testSecond超出作用域1testSecond.obj1testFirst.obj

这样一来,即使两个对象的指针都超出作用域,由于其中彼此的成员变量相互持有彼此的对象而造成循环引用。

__weak修饰符

__weak弱引用(引用计数不会加一 对象随时可能会被dealloc)
内部使用__autoreleasing来维持该对象不被dealloc(这个也是__weak修饰符修饰的对象在所指向的对象销毁之后会自动指向nil的关键所在)
对象的引用计数是记录在一张表上的,不在对象本身或者指针中,系统通过访问这张表来确定是否释放该对象。

将上面相互引用例子中的成员变量变为weak,即可避免相互引用。

weak还有个作用。在持有某对象的弱引用时,若该对象被废弃,则此若引用将自动失效且处于nil被赋值的状态(空弱引用)。

weak提供弱引用,弱引用不持有对象,NSObject对象会被销毁,所以会报一个警告

在这里插入图片描述

我们可以这样使用__weak修饰符的变量,将__strong修饰的对象赋值给__weak修饰的对象,这样就不会发生警告了:

	id __strong objTest = [[NSObject alloc] init];
    id __weak objTestSecond = objTest;

类似于“强弱共舞”

__weak修饰符的引用计数的问题

按以下例子,我们来看一下这两个的引用计数的值:

    id __strong obj = [[NSObject alloc] init];
    id __weak objTest = obj;
    NSLog(@"%lu", CFGetRetainCount((__bridge  CFTypeRef)obj));
    NSLog(@"%lu", CFGetRetainCount((__bridge  CFTypeRef)objTest));

按照常理来说应该是1 1,因为__weak修饰符不持有对象,引用计数值两个都应该是1

打印一下结果:

在这里插入图片描述

在这里插入图片描述

对比一下就能发现 两行NSLog的汇编代码并不一样

第二个__weak修饰符的NSLog在于开始先loadWeakRetained
然后在打印结束后有一个release操作

在打印__weak的引用计数时NSLog先将其以强引用,防止没有打印就释放掉了造成程序的崩溃,在NSLog结束时,会调用objc_release使引用计数减一。

__unsafe_unretained

__unsafe_unretained修饰符正如其名unsafe所示,是不安全的所有权修饰符。
附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象。

为什么是不安全的呢

weak 修饰的指针变量,在指向的内存地址销毁后,会在 Runtime 的机制下,自动置为 nil。 _unsafe_unretain 不会置为 nil,容易出现 悬垂指针,发生崩溃。但是 __unsafe_unretain __weak 效率高。
悬垂指针 指针指向的内存已经被释放了,但是指针还存在,这就是一个 悬垂指针 或者说 迷途指针。野指针,没有进行初始化的指针,其实都是 野指针

附有__unsafe_unretained修饰符的变量同附有__weak修饰符的变量一样,生成的对象会立即释放。

在使用__unsafe_unretained修饰符时,赋值给附有__strong修饰符的变量时有必要确保被赋值的对象确实存在,如果不存在,那么程序就会崩溃。

__autoreleasing修饰符

与MRC进行比较:
MRC中autorelease的使用方法:

  1. 生成并持有NSAutoreleasePool对象
  2. 调用已分配对象的autorelease方法(将对象注册到pool中)
  3. 废弃NSAutoreleasePool对象

在ARC环境下:

NSAutoreleasePool *pool = [NSAutoreleasePool alloc] init]; 1⃣️
[obj autorelease]; 2⃣️
[pool drain]; 3⃣️

等价于:

@autoreleasepool{ 1⃣️
id __autorelease obj2; obj2 = obj; 2⃣️
} 3⃣️

自动调用:

编译器会检查方法名是否以alloc/new/copy/mutableCopy开始,如果不是讲自动将返回值的对象注册到autoreleasepool中

下面情况不使用__autoreleasing修饰符也能使对象注册到autoreleasepool中。

+ (id) array {
	return [[NSMutableArray alloc]init];
}

//如下:
+ (id) array {
	id obj = [[NSMutableArray alloc]init];
	return obj;
}

由于return使得对象变量超出其作用域,所以该强引用对应的自己持有的对象会被自动释放,但该对象作为函数的返回值,编译器也会自动注册到自动释放池

自动调用时的失效过程:

随着obj超出其作用域,强引用失效,所以自动释放自己持有的对象。
同时,随着@autoreleasepool块的结束,注册到autoreleasepool中的所有对象被自动释放。 因为对象的拥有者不存在,所以废弃对象。

weak修饰符与autoreleasing修饰符:

访问__weak修饰符的变量时必须访问注册到autoreleasepool的对象呢,这是因为__weak修饰符只持有对象的弱引用,而在访问引用对象的过程中,该对象有可能被废弃。如果把要访问的对象注册到autoreleasepool中,那么在@autoreleasepool块结束之前都能确保该对象存在,因此,在使用附有__weak修饰符的变量时就必定要使用注册到autoreleasepool中的对象。

id __weak obj1 = obj0;
NSLog(@"class = %@", [obj1 class]);
与以下源代码相同
id __weak obj1 = obj0;
id __autoreleasing tmp = obj1;
NSLog(@"class = %@", [temp class]);

例1

	//这个例子中obj0没有加入到自动释放池中
    id  obj0 = [[NSObject alloc] init];
    id __weak obj1 = obj0;
    NSLog(@"class = %@", [obj1 class]);

在这里插入图片描述

池中没有出现obj0。

例2

	//这个例子中obj0加入了自动释放池中
    id __autoreleasing obj0 = [[NSObject alloc] init];
    id __weak obj1 = obj0;
    NSLog(@"class = %@", [obj1 class]);

在这里插入图片描述

池中出现了obj0

例3

    id __autoreleasing obj0 = [[NSObject alloc] init];
    id __weak obj1 = obj0;
    id __autoreleasing obj2 = obj1;
    NSLog(@"class = %@", [obj2 class]);

在这里插入图片描述

池中出现了obj0,且看到count为2

例4

    id  obj0 = [[NSObject alloc] init];
    id __weak obj1 = obj0;
    id __autoreleasing obj2 = obj1;
    NSLog(@"class = %@", [obj2 class]);

在这里插入图片描述

池中出现了obj0

总结: __weak修饰符并不会将对象加入到自动释放池,但是我们使用__weak修饰的对象一定要是本身已经加入到自动释放池的或者后续使用__autoreleasing将__weak所修饰的对象加入释放池

具体ARC规则:

  • 不能使用retain/release/retainCount/autorelease
  • 不能使用NSAllocateObject/NSDeallocateObject
  • 必须遵守内存管理的方法名规则
  • 不要显式调用dealloc
  • 使用@autorelease块代替NSAutoreleasePool
  • 不能使用区域(NSZone)
  • 对象型变量不能作为C语言结构体的成员
  • 显式转换id和void*

重点:

不能显式调用dealloc

dealloc无法释放不属于该对象的一些东西,需要我们重写时加上去,例如:

  • 通知的观察者,或KVO的观察者
  • 对象强委托/引用的解除(例如XMPPMannerger的delegateQueue)
  • 做一些其他的注销之类的操作(关闭程序运行期间没有关闭的资源)
In the implementation of dealloc, do not call the implementation of superclass. You should try to avoid using dealloc to manage the lifetime of limited resources, such as file descriptors.
You never send a dealloc message directly. Instead, the dealloc method of the object is called by the runtime.
在dealloc的实现中,不要调用超类的实现。您应该尽量避免使用dealloc管理有限资源(如文件描述符)的生存期。
不要直接发送dealloc消息。与直接发送dealloc消息不同,对象的dealloc方法由运行时调用。

Special Considerations
When not using ARC, your implementation of dealloc must invoke the superclass’s implementation as its last instruction.
特别注意事项
当不使用ARC时,dealloc的实现必须调用父类(super)的实现作为它的最后一条指令 [super dealloc]

__bridge

  • bridge可以实现Objective-C与C语言变量和Objective-C与CoreFoundation对象之间的互相转换
  • _bridge不会改变对象的持有状况,既不会retain,也不会release
  • _bridge转换需要慎重分析对象的持有情况,稍不注意就会内存泄漏
  • _bridge_retained用于将口C变量转换为C语言变量或将OC对象转换为CoreFoundation对象
  • _bridge_retained类似于retain,“被转换的变量”所持有的对象在变量赋值给"转换目标变量“后持有该对象
  • _bridge_transfer用于将C语言变量转换为OC变量或将CoreFoundation对象转换为OC对象
  • bridge_transfer类似于release,“被转换的变量”所持有的对象在变量赋值给“转换目标变量”后随衣释热

属性关键字和所有权修饰符

属性关键字所有权修饰符
assign_unsafe_unretained
copy__strong
retain__strong
strongstrong
__unsafe_unretained__unsafe_unretained
weak__weak

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

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

相关文章

LeetCode 热题 100(一):哈希。49. 字母异位词分组、128. 最长连续序列。

LeetCode100链接&#xff1a;LeetCode 热题 100 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 一、49. 字母异位词分组 题目要求&#xff1a; 给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。…

【ACM】—蓝桥杯大一暑期集训Day2

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前正在学习C/C、Java、算法等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL&#xff…

10秒搞定!教你如何轻松压缩jpg格式图片大小!

大家在日常拍照时&#xff0c;都会发现拍摄出来的JPG图片体积比较大&#xff0c;使用和保存时都会比较麻烦。那么该怎样压缩图片大小呢&#xff1f; 首先&#xff0c;我们需要了解什么是JPG压缩。JPG是一种有损压缩格式&#xff0c;通过减少图像中的信息量来使文件大小缩小。使…

Java实现多文件上传及进度条提示-源码下载

1、方案概述 1、案例框架使用的是springmvc5.3.16版本,jackson使用的是2.13.3版本。 2、前端使用的是Layui2.8.11框架,这个框架上手较为容易。 3、使用关键类CommonsMultipartResolver和MultipartHttpServletRequest实现多文件捕获。 2、效果预览 【选择文件】 【上传过…

Android JNI线程的同步 (十三)

🔥 Android Studio 版本 🔥 🔥 了解线程同步的两个变量 🔥 pthread_mutex_t 互斥锁 线程的互斥: 目前存在两个线程 , 线程A和线程B, 只允许只有一个资源对临界资源进程操作 (大概意思就是 : A线程 进入操作临界资源的时候 , 那么 B线程 就要进行等待 . 等到 A线程…

感受C++模版的所带来的魅力,扎实基础,扩展思维

一、泛型编程思想 首先我们来看一下下面这三个函数&#xff0c;如果学习过了 C函数重载 和 C引用 的话&#xff0c;就可以知道下面这三个函数是可以共存的&#xff0c;而且传值会很方便void Swap(int& left, int& right) {int temp left;left right;right temp; } …

Nuxt.js--》解密Nuxt.js:构建优雅、高效的现代化Vue.js应用

博主今天开设Nuxt.js专栏&#xff0c;带您深入探索 Nuxt.js 的精髓&#xff0c;学习如何利用其强大功能构建出色的前端应用程序。我们将探讨其核心特点、灵活的路由系统、优化技巧以及常见问题的解决方案。无论您是想了解 Nuxt.js 的基础知识&#xff0c;还是希望掌握进阶技巧&…

【C++】Eigen库实现最小二乘拟合

前言 入职第二周的任务是将导师的Python代码C化&#xff0c;发现Python中存在Numpy包直接调用np.polyfit就好了&#xff0c;但是C不存在需要造轮子。 #include <iostream> #include <cmath> #include <vector> #include <Eigen/QR> #include "x…

re学习(15)BUUCTF 2019红帽杯easyRe(寻找数据+xor问题)

参考视频&#xff1a; 【BUUCTF】每天一个CTF11“2019红帽杯easyRe”_哔哩哔哩_bilibili &#xff08;本人觉得看视频比看博客效率能提高十倍&#xff0c;呜呜呜&#xff0c;还是视频香~~~与君共勉&#xff09; 下载地址&#xff1a; BUUCTF在线评测 前言&#xff1a;虽然…

Spring Boot集成Redisson实现分布式锁

Spring Boot集成Redisson实现分布式锁 在分布式系统中&#xff0c;为保证数据的一致性和并发访问的安全性&#xff0c;我们经常会使用分布式锁来协调多个节点之间对共享资源的访问。Redisson是一个基于Redis的Java驻内存数据网格&#xff08;In-Memory Data Grid&#xff09;和…

【C++】string类模拟

文章目录 成员变量和查看接口迭代器&#xff08;读和读写&#xff09;operator[]&#xff08;读和读写&#xff09;c_str()size() 构造函数用字符串构造用对象构造&#xff08;两种方法&#xff09;析构 赋值运算符重载扩容和调整reserve()resize()clear() 增删查改push_back()…

C++的switch函数用法

一个 switch 语句允许测试一个变量等于多个值时的情况。每个值称为一个 case&#xff0c;且被测试的变量会对每个 switch case 进行检查。 语法 C 中 switch 语句的语法&#xff1a; switch(expression){ case constant-expression : statement(s); break; // 可选的 case c…

Redis数据类型 — Zset

目录 Zset内部设计 跳表哈希表 ZipList ZSet中每一个元素都需要指定一个score值和member值&#xff1a;<1> 可以根据score值排序后<2> member必须唯一<3> 可以根据member查询分数 Zset内部设计 因此&#xff0c;zset底层数据结构必须满足键值存储、键必…

AutoCAD如何通过C#进行插件开发?

文章目录 0.引言1.开发工具准备2.VS&#xff08;C#&#xff09;创建插件3.使用插件 0.引言 AutoCAD是一款广泛应用于工程设计和建筑行业的计算机辅助设计软件。通过使用C#语言进行插件开发&#xff0c;可以扩展AutoCAD的功能&#xff0c;实现定制化的需求。插件可以实现自动化绘…

linux之Ubuntu系列(-)

单用户和多用户 注意事项 Linux 系统 中区分大小写的 Window 系统 不分区大小写的

1.Java语言概述

1.1概述 JDK(Java development kit)java开发工具包 JDK是提供Java开发人员使用的&#xff0c;其中包含java的开发工具&#xff0c;包括JRE,所以安装JDK&#xff0c;就不需要安装JRE了 其中编译工具javac.exe、打包工具jar.exe JRE(Java Runtime environment) java运行环境 包括…

android11 input 事件 1 初始化

system_server 初始化InputManagerService // SystemServer.java private void startOtherServices(NonNull TimingsTraceAndSlog t) {t.traceBegin("startOtherServices");// 初始化InputManagerServicet.traceBegin("StartInputMana…

让机器学习不再是过门不入,带您一起详解机器学习(机器学习 Machine Learning 研习之一)

什么是机器学习&#xff08;Machine Learning&#xff09;&#xff1f; 机器学习是]赋予计算机学习能力的研究领域 无需明确编程。 ——阿瑟塞缪尔&#xff0c;1959 计算机程序可以从关于某些任务的经验 E 中学习 T 和一些绩效衡量 P&#xff0c;如果其在 T 上的绩效按 P 衡量&…

微信批量自动加好友神器,多账号如何统一自动加好友

其实这样的方式不仅太集中容易造成频繁 效率还很低 现在可以解放双手 只需要你有数据 导入数据设置相应添加任务 就可以每天根据你设置的时间点去添加好友 直到数据被用完 就不用每天手动一个个去添加咯 还可以多账号统一自动加好友 一天就可以加多点 相信 无论是个…

RDS-Tools RDS-Knight Crack

RDS 高级安全性 利用全面的网络安全工具箱中有史以来最强大的安全功能集来保护您的 RDS 基础架构。 全方位 360 保护 无与伦比的功能集 无与伦比的物有所值 企业远程桌面安全。现代工作空间的智能解决方案。 办公室正在权力下放。远程办公室和移动员工数量创历史新高。随…