【Effective Objective - C 2.0】——读书笔记(五)

文章目录

  • 二十九、理解引用计数
  • 三十、以ARC简化引用计数
  • 三十一、在dealloc方法中只释放引用并解除监听
  • 三十二、编写异常安全代码时留意内存管理问题
  • 三十三、以弱引用避免保留环
  • 三十四、以”自动释放池块“降低内存峰值
  • 三十五、用"僵尸对象"调试内存管理问题
  • 三十六、不要使用retainCount


二十九、理解引用计数

Objective-C 语言使用引用计数来管理内存,也就是说,每个对象都有个可以递增或递减的计数器,如果某个对象引用他时就会给其引用计数加1,用完了之后,就递减其计数,直至为0,销毁这个对象。ARC实际上也是一种引用计数机制。

引用计数工作原理

在引用计数架构下,对象有个计数器,用以表示当前有多少个事物想令此对象继续存活下去。NSObject协议声明了下面三个方法用于操作计数器:

Retain 递增保留计数
release 递减保留计数
autorelease 待稍后清理“自动释放池”时,再递减保留计数。

对象创建出来时,其保留计数至少为1。若想令其继续存活,则调用retain 方法。要是某部分代码不再使用此对象,不想令其继续存活,那就调用releaseautorelease方法。最终当保留计数归零时,对象就回收了(deallocated),也就是说,系统会将其占用的内存标记为“可重用”。此时,所有指向该对象的引用也都变得无效了。

在这里插入图片描述

在图5-2所示的对象图中,ObjectB与ObjectC都引用了ObjectA。若ObjectB 与ObjectC 都 不 再 使 用 O b j e c t A , 则 其 保 留 计 数 降 为 0 , 于 是 便 可 摧 毁 了。

在这里插入图片描述

为了避免在不经意间使用了无效对象,一般relase之后都会清空指针,这样能保证不出现悬挂指针

 NSMutableArray *array = [[NSMutableArray alloc] init];
    NSNumber *number = [[NSNumber alloc] initWithInt:2023];
    [array addObject:number];
    [number release];
    number = nil;//避免悬垂指针

属性存取方法中的内存管理
属性存取方法也存在内存管理模式,对象也可以保留别的对象,这一般通过访问属性来实现,而对于实例变量中的属性为strong关系的时候设置的值会保留

@property (nonatomic, strong)NSString *foo;
- (void)setFoo:(id)foo {
    [foo retain];//保留新值
    [_foo release];//释放旧值
    _foo = foo;//更新实例变量
}

此方法将保留新值并释放旧值,然后更新实例变量,令其指向新值。顺序很重要,我们不能先释放旧的值,否则对象那么有可能在release之后就被销毁了,可能存在悬空指针的现象

自动释放池

在Objective-C 的引用计数架构中,自动释放池是一项重要特性。调用release 会立刻递 减对象的保留计数 ( 而且还有可能令系统回收此对象),然而有时候可以不调用它,改为调用 autorelease,此方法会在稍后递减计数,通常是在下一次“事件循环” (event1oop)时递减, 不 过 也 可 能 执 行 得 更 早 些 (參 见 第 3 4 条 )。

- (NSString *)stringValue {
   NSString *str = [[NSString alloc] initWithFormat:@"I am this:%@", self];
   return str;
}

调用此方法时会让引用计数多1,我们需要设法抵消这多出来的1,这就需要用到autorelease

此时返回的str对象其保留值比期望多1 因为alloc会➕1,然后并没有进行释放,意味着调用者可能背负的操作更多,但又不能在返回前就release str,所以代码可以这样写

- (NSString *)stringValue {
   NSString *str = [[NSString alloc] initWithFormat:@"I am this:%@", self];
   return [str autorelease];
}

实际上,释放操作会在清空最外层的自动释放池时执行,除非你有自己的自动释放池,否则这个时机指的就是当前线程的下一次事件循环。
通过上述可见,autorelease能延长对象生命期,使其在跨越方法调用边界后依然可以存活一段时间。

保留环
保留环其实就是因为对象之间的互相引用出现的问题,这会导致内存泄漏,因为循环中的对象其保留计数不会降为0。
在这里插入图片描述
我们要解决保留环,通常采用“弱引用”来解决此问题,或者从外界命令循环中的某个对象不在保留另一个对象。

要点

  • 引用计数机制通过可以递增递减的计数器来管理内存。对象创建好之后,其保留计数至少为1。若保留计数为正,则对象继续存活。当保留计数降为0时,对象就被销毁了。
  • 在对象生命期中,其余对象通过引用来保留或释放此对象。保留与释放操作分别会递增及递减保留计数。

三十、以ARC简化引用计数

使用ARC时一定要记住,引用计数实际上 还是要执行的,只不过 保 留 与 释 放 操 作 现 在 是由ARC 自动为你添加。稍后将会看到,除了为方法所返回的对象正确运用内存管理语义 之外,ARC 还有更多的功能。 不过,ARC 的那些功能都是基 于核心的内存管理语义而构建的, 这套标准语义贯穿于整 个Objective-C语言。

由于ARC会自动执行retain 、release 、autorelease 等操作,所以直接在ARC下调用这些 内存管理方法是非法的。具体来说,不能调用下列方法:

  • retain
  • release
  • autorelease
  • dealloc

实际上,ARC 在调用这些方法时,并不通过普通的Objective-C 消息派发机制,而是直 接调用其底层C 语言版本。这样做性能更好,因为保留及释放操作需要频繁执行,所以直 接 调 用 底 层 丽 数 能 节省很多CPU周期。比方说,ARC会调用与retain等价的底层函数 objc_retain。这也是不能覆写retain、release 或autorelease 的缘由,因为这些方法从来不会被直接 调用。笔者在本节后面的文字中将用等价的Objective-C 方法来指代与之相关的底层C语言 版本,这对 于那些 手动管理过引用计数的开发者来说更易理解。

使用ARC时必须遵守的方法命名规则

若方法名以下列词语开头,则其返回的对象归调用者所有:

  • alloc
  • new
  • copy
  • mutableCopy

意思就是调用这四种方法的那段代码要负责释放方法所返回的对象。也就是说,这些对象的保留计数是正值,而调用了这四种方法的那段代码要将其中一次保留操作抵消。若方法名不以上述四个词语开头,则表示其所返回的对象并不归调用者所有。这种情况下,返回的对象会自动释放,也就是使用了autorelease。

ARC除了自动调用“保留”和“释放”方法外,其也可以优化操作,比如两个在一起的保留和释放,它就会将这一对直接移除,不执行这两个代码。

在ARC环境下编译代码时,必须考虑“向后兼容性”,以兼容那些不使用ARC的代码,其实ARC的简化操作是因为其调用的特殊函数,它会把autorelease方法改为调用objc_autoreleaseReturnValue函数,把retain方法改为objc_retainAutoreleaseReturnValue函数。

变量的内存管理语义

  • ARC也会处理局部变量和实例变量的内存管理,默认情况喜爱每个变量都是指代对象的强引用,一定要理解的事实例变量的真正意思是什么。对于某些代码来说,语义和手动内存管理存在一定差异。例如在编写setter方法的时候,不用ARC模式是这样写
- (void)setObject:(id)object {
    [_object release];
    _object = [object retain];
}

这样写的问题是新值和实例变量的值是一样的,只有当前对象还在用引用这个值的时候,那么设置方法中的释放操作会令值保留计数变为0,那么就会被系统回收从而产生crash,接下来retain操作则是错误的,使用ARC的时候不会存在这种流失的现象

- (void)setObject:(id)object {
    _object = object;
}

ARC会用一种安全的方式来设置,先保留新值,再释放旧值,最后设置实例变量

在这里插入图片描述

ARC如何清理实例变量

要管理其内存,ARC就必须在“回收分配给对象的内存”是生产必要的清理代码。ARC环境下,dealloc方法可以这样来写:

- (void)dealloc {
	CFRelease ( _coreFoundationObject);
	free ( _heapAllocatedMemoryBlob);
}

因为ARC会自动生成回收对象时所执行的代码,所以通常无需再编写dealloc方法。这能减少项目源代码的大小,而且可以省去其中一些样板代码。

要点:

  • 有ARC之后,程序员就无须担心内存管理问题了。使用ARC来编程,可省去类中的许多“样板代码”。
  • ARC管理对象生命期的办法基本上就是:在合适的地方插入“保留”及“释放”操作。在ARC环境下,变量的内存管理语义可以通过修饰符指明,而原来则需要手工执行“保留”及“释放”操作。
  • 由方法所返回的对象,其内存管理语义总是通过方法名来体现。ARC将此确定为开发者必须遵守的规则。
  • ARC只负责管理OC对象的内存。尤其要注意CoreFoundation对象不归ARC管理,开发者必须适时调用CFRetain/CFRelease。

三十一、在dealloc方法中只释放引用并解除监听

对象在经历其生命期后,最终会为系统所回收,这时就要执行dealloc 方法了。在每个 对象的生命期内,此方法仅执行一次,也就是当保留计数降为0的时候。然而具体何时执 行,则无法保证。也可以理解成:我们能够通过人工观察保留操作与释放操作的位置,来预 估此方法何时即将执行。但实际 上,程序库会以开发者察觉不到的方式操作对象,从而使回 收对象的真正时机和预期的不同。你决不应该自己调用dealloc 方法。运行期系统会在适当 的时候调用它。而且, 一旦调用过dealloc之后,对象就不再有效了,后续方法调用均是无效的。

那么,应该在dealloc 方法中做些什么呢?主要就是释放对象所拥有的引用,也就是把 所 有 Objective-C对象都释放掉,ARC会通过自动生成的.cxx_destruct方法 (参见第30条), 在dealloc 中为你自动添加这些释放代码。对象所拥有的其他非Objective-C 对象也要释放。 比如CoreFoundation 对象就必领手工释放,因为它们是由纯C的API 所生成的。

在dealloc 方法中,通常还要做一件事,那就是把原来配置过的观测行为(observation behavior )都清理掉。如果用NSNotificationCenter 给此对象订阅(register)过某种通知,那么 一般应该在这里注销(unregister ),这样的话,通知系统就不再把通知发给回收后的对象了, 若是还向其发送通知,则必然会令应用程序崩溃。

dealloc 方法可以这样来写:

- (void)dealloc {
	CFRelease(coreFoundationObject);
	[[NSNotificationCenter defaultCenter] removeObserver:self];
}

要点:

  • 在dealloc方法里,应该做的事情就是释放指向其他对象的引用,并取消原来订阅的“键值观测”或“NSNotificationCenter”等通知,不要做其他事情。

  • 如果对象持有文件描述符等系统资源,那么应该专门编写一个方法来释放此种资源。这样的类要和其使用者约定:用完资源后必须调用close方法。

  • 执行异步任务的方法不应在dealloc里调用;只能在正常状态下执行的那些方法也不应在dealloc里调用,因为此时对象已处于正在回收的状态了。

关键是释放引用并且解除监听

三十二、编写异常安全代码时留意内存管理问题

要点

  • 捕获异常时,一定要注意将 try 块内所创立的对象清理干净。
  • 在默认情况下,ARC不生成安全处理异常所需的清理代码。开启编译器标志后,可生成这种代码,不过会导致应用程序变大,而且会降低运行效率。

三十三、以弱引用避免保留环

  • 保留环这个词出现了很多次了,究极原因是对象之间的相互引用造成的
  • 最简单的保留环可以由两个对象构成,它们互相引用对方

在这里插入图片描述

  • 对于如下的代码可以看出引用环的存在
    在这里插入图片描述
    在这里插入图片描述

保留环导致内存泄漏

保留环会导致内存泄漏,如果只剩一个引用还指向保留环的实例,而现在这个又把引用一处,那么整个环就会泄漏,也就是说无法继续访问其中的任何对象了

学会避免保留环的出现

  • 避免保留环的最佳方式就是弱引用。这种引用通常用来表示“非拥有关系“,将属性声明为unsafe_unretained即可,当然注意unsafe_unretained修饰的属性同assgin特质等价
#import <Foundation/Foundation.h>

@class EOCClassA;
@class EOCClassB;

@interface EOCClassA : NSObject
@property(nonatomic,strong)EOCClassB *other;
@end

@interface EOCClassB: NSObject
@property(nonatomic,unsafe unretained) EOCClassA *other;
@end


  • OC还有一项与ARC相伴的运行期的特性,叫弱引用weak,与unsafe_unretained作用完全相同,在现在的ARC下基本取代了unsafe_unretained关键字。对于weak只要系统回收了属性,属性值自然置nil。

weak 和unsafe_unretained区别
在这里插入图片描述

要点

  • 将某些引用设为weak,可避免出现“保留环”。
  • weak引用可以自动清空,也可以不自动清空。自动清空是随着ARC而引入的新特性,由运行期系统来实现。在具备自动清空的弱引用上,可以随意读取其数据,因为这种引用不会指向已经回收过的对象。

三十四、以”自动释放池块“降低内存峰值

Objective-C对象的生命期取决于其引用计数(参见第29条)。在 Objective-C的引用计数架构中,有一项特性叫做"自动释放池"(autorelease pool)。释放对象有两种方式;一种是调用release 方法,使其保留计数立即递减;另一种是调用 autorelease 方法,将其加入"自动释放池"中。自动释放池用于存放那些需要在稍后某个时刻释放的对象。清空(drain)自动释放池时,系统会向其中的对象发送 release 消息。

创建自动释放池所用语法如下∶

@autoreleasepool {
	// ...
}

然而,一般情况下无须担心自动释放池的创建问题。Mac OS X与 iOS 应用程序分别运行于Cocoa 及 Cocoa Touch 环境中。系统会自动创建一些线程,比如说主线程或是"大中枢派发"(Grand Central Dispatch,GCD)③机制中的线程,这些线程默认都有自动释放池,每次执行"事件循环"(event loop)时,就会将其清空。因此,不需要自己来创建"自动释放池块"。通常只有一个地方需要创建自动释放池,那就是在 main 函数里,我们用自动释放池来包裹应用程序的主入口点((main application entry point)。比方说,iOS程序的 main 函数经常这样写∶

int main(int argc,char *argv[]) {
	@autoreleasepool {
		return UIApplicationMain (argc, argV, ni1, @"EOCAppDelegate");
	}
}	

比方说,要从数据库中读出许多对象。代码可能会这么写∶

NSArray*databaseRecords =/* ...*/;
NSMutableArray *people = [NSMutableArray new];
for (NSDictionary *record in databaseRecords) {
	EOCPerson *person = [ [EOCPerson alloc] initWithRecord: record];
	[people addObject:person];
}

这就意味着在执行for 循环时。会持续有新对象创建出来。并加入自动释放池中。所有这种对象都要等 for 循环执行完才会释放。这样一来,在执行 for 循环时,应用程序所占内存量就会持续上涨,而等到所有临时对象都释放后,内存用量又会突然下降。

EOCPerson的初始化函数也许会像上例那样,再创建出一些临时对象。若记录有很多条,则内存中也会有很多不必要的临时对象,它们本来应该提早回收的增加一个自动释放池即可解决此问题。如果把循环内的代码包裹在"自动释放池块"中,那么在循环中自动释放的对象就会放在这个池,而不是线程的主池里面。例如∶

NSArray *databaseRecords = /*...*/;
NSMutableArray *people = [NSMutableArray new];
for(NSDictionary *record in databaseRecords)[
	@autoreleasepool {
		EOCPerson *person = [[EOCPerson alloc] initWithRecord:record];
		[people addObject:person];
	}	
}

加上这个自动释放池之后,应用程序在执行循环时的内存峰值就会降低,不再像原来那么高了。内存峰值(high-memory waterline)是指应用程序在某个特定时段内的最大内存用量(highest memory footprint)。新增的自动释放池块可以减少这个峰值,因为系统会在块的末尾把某些对象回收掉。而刚才提到的那种临时对象,就在回收之列。
自动释放池机制就像"栈"(stack)一样。系统创建好自动释放池之后,就将其推入栈中,而清空自动释放池,则相当于将其从栈中弹出。在对象上执行自动释放操作,就等于将其放入栈顶的那个池里。

要点

  • 自动释放池排布在栈中,对象收到 autorelease 消息后,系统将其放入最顶端的池里。
  • 合理运用自动释放池,可降低应用程序的内存峰值。
  • @autoreleasepool 这种新式写法能创建出更为轻便的自动释放池。

三十五、用"僵尸对象"调试内存管理问题

调试内存管理问题很令人头疼。大家都知道,向业已回收的对象发送消息是不安全的。这么做有时可以,有时不行。具体可行与否,完全取决于对象所占内存有没有为其他内容所覆写。而这块内存有没有移作他用,又无法确定,因此,应用程序只是偶尔崩溃。在没有崩溃的情况下,那块内存可能只复用了其中一部分,所以对象中的某些二进制数据依然有效。还有一种可能,就是那块内存恰好为另外一个有效且存活的对象所占据。在这种情况下,运行期系统会把消息发到新对象那里。而此对象也许能应答。也许不能。如果能。那程序就不崩溃,可你会觉得奇怪∶为什么收到消息的对象不是预想的那个呢?若新对象无法响应选择子,则程序依然会崩溃。

所幸 Cocoa提供了"僵尸对象"(Zombie Object)这个非常方便的功能。启用这项调试功能之后,**运行期系统会把所有已经回收的实例转化成特殊的"僵尸对象"、而不会真正回收它们。**这种对象所在的核心内存无法重用,因此不可能遭到覆写。僵尸对象收到消息后,会抛出异常,其中准确说明了发送过来的消息,并描述了回收之前的那个对象。僵尸对象是调试内存管理问题的最佳方式。

在这里插入图片描述

僵尸对象的工作原理:

系统在即将回收对象时,如果发现通过环境变量启用了僵尸对象功能,那么还将执行一个附加步骤。这一步就是把对象转化为僵尸对象,而不彻底回收。

僵尸类是怎么产生的:

他其实是在运行期生成的,当首次碰到一个类的对象要变成僵尸对象时,它就会创建这么一个类。僵尸类是从名为_NSZombie_的模版里复制出来的,这些僵尸类没有多少事情可做,只是一个标记,又因为它将原类的方法都拷贝了,所以它会响应原类的方法,不过它会报错提醒程序员。

要点:

  • 系统在回收对象时,可以不将其真的回收,而是把它转化为僵尸对象。通过环境变量NSZombieEnabled 可开启此功能。
  • 系统会修改对象的 isa 指针,令其指向特殊的僵尸类,从而使该对象变为僵尸对象。僵尸类能够响应所有的选择子,响应方式为∶打印一条包含消息内容及其接收者的消息,然后终止应用程序。

三十六、不要使用retainCount

在ARC模式下该方法已经被弃用了,首要原因还是它返回的保留计数只是某个对象在某个具体时间的引用计数,完全没有特殊的参考意义,现在不仅存在自动释放池,还有ARC模式的自动管理引用计数,那么retainCOunt完全不能反映真实的引用计数。

要点

  • 对象的保留计数看似有用,实则不然,因为任何给定时间点上的“绝对保留计数”都无法反映对象生命期的全貌。
  • 引入ARC之后,retainCount方法就正式废止了,在ARC下调用该方法会导致编译器报错。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】实现队列

目录 &#xff08;一&#xff09;队列 &#xff08;二&#xff09;头文件 &#xff08;三&#xff09; 功能实现 &#xff08;1&#xff09;初始化 &#xff08;2&#xff09; 销毁队列 &#xff08;3&#xff09; 入队 &#xff08;4&#xff09;出队 &#xff08;5&a…

论文阅读:四足机器人对抗运动先验学习稳健和敏捷的行走

论文&#xff1a;Learning Robust and Agile Legged Locomotion Using Adversarial Motion Priors 进一步学习&#xff1a;AMP&#xff0c;baseline方法&#xff0c;TO 摘要&#xff1a; 介绍了一种新颖的系统&#xff0c;通过使用对抗性运动先验 (AMP) 使四足机器人在复杂地…

luigi,一个好用的 Python 数据管道库!

🏷️个人主页:鼠鼠我捏,要死了捏的主页 🏷️付费专栏:Python专栏 🏷️个人学习笔记,若有缺误,欢迎评论区指正 前言 大家好,今天为大家分享一个超级厉害的 Python 库 - luigi。 Github地址:https://github.com/spotify/luigi 在大数据时代,处理海量数据已经成…

NVIDIA 刚刚揭秘了他们的最新大作——Eos,一台跻身全球十强的超级计算机

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

android获取sha1

1.cmd在控制台获取 切换到Android Studio\jre\bin目录下执行keytool -list -v -keystore 签名文件路径例如&#xff1a; 2.也可以在android studio中获取 在Terminal中输入命令&#xff1a;keytool -list -v -keystore 签名文件路径获取 获取到的sha1如下&#xff1a;

ubuntu屏幕小的解决办法

1. 安装vmware tools , 再点自适应客户机 执行里面的vmware-install.pl这个文件 &#xff1a;sudo ./vmware-install.pl 执行不了可以放到家目录&#xff0c;我放在了/home/book 里面 最后点这个自适应客户机 然后我这里点不了是因为我点了控制台视图和拉伸客户机&#xff0c…

ClickHouse--07--Integration 系列表引擎

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Integration 系列表引擎1 HDFS1.1 语法1.2 示例&#xff1a; 2 MySQL2.1 语法2.2 示例&#xff1a; 3 Kafka3.1 语法3.2 示例&#xff1a;3.3 数据持久化方法 Integ…

Leetcode 94.二叉树的中序遍历

题目 给定一个二叉树的根节点 root &#xff0c;返回 它的中序 遍历 。 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2] 进阶: 递归算法很简单&#xff0c;你可以通过迭代算法完成吗&#xff1f; 递归遍历 递归遍历就是一个很简单的中序遍历 /*** Definitio…

LEETCODE 69. x 的平方根

class Solution { public:int mySqrt(int x) {int left0;int rightx;int midleft(right-left)/2;int ans-1;while(left<right){midleft(right-left)/2;if((long long)mid*mid<x){ansmid;leftmid1;}else{rightmid-1;}}return ans;} };*(long long)

GPT SOVITS项目 一分钟克隆 (文字输出)

步骤流程&#xff1a;&#xff08;首先使用UVR 提取人声文件&#xff0c;然后按下面步骤进行&#xff09; 注意这里提交的音频是参考的音频

【简写MyBatis】01-简单映射器

前言 新开一个坑&#xff0c;为了学习一下MyBatis的源码&#xff0c;写代码是次要的&#xff0c;主要为了吸收一下其中的思想和手法。 目的 关联对象接口和映射类的问题&#xff0c;把 DAO 接口使用代理类&#xff0c;包装映射操作。 知识点 动态代理简单工厂模式Invocati…

Web APIs -05

js执行机制 js是单线程&#xff0c;同一个时间只能做一件事情&#xff0c;所有任务需要排队所以有时候会渲染不连贯 同步任务 都在主线程上执行&#xff0c;形成一个执行栈 异步任务 js的异步是通过回调函数实现的分为三类&#xff1a;1.普通事件&#xff1a;click等&…

课时31:内容格式化_输出格式化_printf格式化

1.1.3 printf格式化 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习。 基础知识 场景需求 虽然我们能够通过 echo的方式实现信息某种程度的格式化输出&#xff0c;但是很多信息的输出偏重于手工的干预&#xff0c;效率太慢。我们需要一种功能…

“构建安全高效的前端权限控制系统:确保用户访问合适的内容“

✨✨ 欢迎大家来到喔的嘛呀的博客✨✨ &#x1f388;&#x1f388;希望这篇博客对大家能有帮助&#x1f388;&#x1f388; ✨✨ 一个正在努力的人&#xff0c;期待您的关注✨✨ 目录 引言 一、背景介绍 二 、具体实现方法 &#xff08;1&#xff09;用户角色管理 1. 安装依…

FL Studio21.2水果软件官方中文版网站

FL Studio 21&#xff0c;通常被称为“水果”音乐制作软件&#xff0c;是一款功能强大的数字音频工作站&#xff08;DAW&#xff09;。这款软件起源于“Fruity Loops”&#xff0c;最初的设计初衷是针对采样循环操作&#xff0c;自1998年发布以来&#xff0c;它已经从一款针对M…

Rust中不可变变量与const有何区别?

Rust作者认为变量默认应该是immutable&#xff0c;即声明后不能被改变的变量。这一点是让跨语言学习者觉得很别扭&#xff0c;不过这一点小的改变带来了诸多好处&#xff0c;本节我们来学习Rust的变量。 什么是变量&#xff1f; 如果你初次学习编程语言&#xff0c;变量会是一…

stm32:pwm output模块,记录一下我是用smt32,输出pwm波的记录--(实现--重要)

我是实现了输出pwm波&#xff0c;频率固定&#xff0c;占空比可以不断调整的方法&#xff0c;将PA0接到示波器上&#xff0c;可以看到是一个标准的PWM波&#xff0c;如图下面示波器图。 1&#xff0c;首先是ioc的配置 我刚开始设置的分频的倍数是7199&#xff0c;使得分频的太…

【项目实现】自主HTTP服务器

自主HTTP服务器 项目介绍网络协议栈介绍协议分层 数据的封装与分用数据的封装与分用 HTTP相关知识介绍HTTP的特点 URL格式URI、URL、URNHTTP的协议格式HTTP的请求方法HTTP的状态码HTTP常见的Header CGI机制介绍CGI机制的概念CGI机制的实现步骤CGI机制的意义 日志编写套接字相关…

嵌入式内核链表list_head,如何管理不同类型节点的实现

在Linux内核中&#xff0c;提供了一个用来创建双向循环链表的结构 list_head。虽然linux内核是用C语言写的&#xff0c;但是list_head的引入&#xff0c;使得内核数据结构也可以拥有面向对象的特性&#xff0c;通过使用操作list_head 的通用接口很容易实现代码的重用&#xff0…

NBlog个人博客部署过程记录 -- 后端springboot + 前端vue

项目是fork的Naccl大佬NBlog项目&#xff0c;页面做的相当漂亮&#xff0c;所以选择了这个。可以参考2.3的效果图 惭愧&#xff0c;工作两年了也每个自己的博客系统&#xff0c;趁着过年时间&#xff0c;开始搭建一下. NBlog原项目的github链接&#xff1a;Naccl/NBlog: &#…