XSLT存档  

不及格的程序员-八神

 查看分类:  ASP.NET XML/XSLT JavaScripT   我的MSN空间Blog
Github资料整理员
只求星,https://github.com/0voice
 

Objective-C的MRC手动内存管理

0x01 自动释放池的创建

autorelease

NSObject类提供了一个autorelease方法,该方法预先设定了一条会在未来某个时间发送的release消息,其返回值是接收这条消息的对象:

- (id) autorelease;

当给一个对象发送autorelease消息时,实际上是将该对象添加到了自动释放池中。

当自动释放池被销毁时,会向该池中的所有对象发送release消息。

NSAutoreleasePool对象

使用NSAutoreleasePool对象可以创建一个自动释放池,在创建和释放NSAutoreleasePool对象之间的代码都会使用该池:

NSAutoreleasePool *pool;

pool = [NSAutoreleasePool new];
//Coding in here...
[pool release];

 

0x02 自动释放池的工作原理

在我们创建基于Foundation的项目时,其实编译器已经自动帮我们创建了@autorelease的自动释放池:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

1、当我们把代码写在@autoreleasepool的花括号里时,所有的代码都是自动释放的;

2、任何在@autoreleasepool的花括号定义的变量,在括号外是无法使用的,类似于C语言的数据有效范围;

3、iOS 5.0之后已经淘汰使用NSAutoreleasePool对象创建自动释放池的方法,因为@autoreleasepool更直观,而且Objective-C语言创建和释放内存的能力远在我们之上。

4、autorelease只是把对象扔到自动释放池里面(延迟了对象被销毁的时间),并不可以改变对象的计数器;

5、改变计数器只有retain和release两种方法;

6、autorelease和release不能同时存在:

autoreleasepool{
      Car *c = [[[Car alloc] init] autorelease];
      [c release];
}

//错误!!!

//对对象执行二次释放会出现野指针错误!!!

7、autoreleasepool是以栈结构存在的,栈结构的特点就是先进后出;

8、autorelease的缺点是不能精确控制对象被销毁的时间,内存处理效率低(适用于占用内存比较小的对象,对象里面包含的属性主要是基本数据类型)。如果是在一个循环中出现大量的迭代,必须自己在循环中创建一个自动释放池来定期清理:

NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init]; 
 
int i;
 
for (i = 0; i < 1000000; i++) {
    id object = [someArray objectAtIndex: i]; 
    NSString *desc = [object descrption];
 
// and do something with the description 
    if (i % 1000 == 0) {
     [pool release];
     pool = [[NSAutoreleasePool alloc] init];
     }
}
[pool release];

9、在调用函数的过程中自动释放池不会被销毁,程序不会随即销毁自动释放池,因此不必保留使用的每一个对象;

10、自动释放池被清理的时间是完全确定的:要么在代码中自己手动销毁,要么是使用APPKit时在时间循环结束时销毁。

 

C++ 智能指针 shared_ptr更多智能指针

shared_ptr 是C++11提供的一种智能指针类,它足够智能,可以在任何地方都不使用时自动删除相关指针,从而帮助彻底消除内存泄漏和悬空指针的问题。

它遵循共享所有权的概念,即不同的 shared_ptr 对象可以与相同的指针相关联,并在内部使用引用计数机制来实现这一点。

每个 shared_ptr 对象在内部指向两个内存位置:

  1. 指向对象的指针。
  2. 用于控制引用计数数据的指针。

共享所有权如何在参考计数的帮助下工作:

  1. 当新的 shared_ptr 对象与指针关联时,则在其构造函数中,将与此指针关联的引用计数增加1。
  2. 当任何 shared_ptr 对象超出作用域时,则在其析构函数中,它将关联指针的引用计数减1。如果引用计数变为0,则表示没有其他 shared_ptr 对象与此内存关联,在这种情况下,它使用delete函数删除该内存。

创建 shared_ptr 对象

使用原始指针创建 shared_ptr 对象

std::shared_ptr<int>p1(newint());

上面这行代码在堆上创建了两块内存:1:存储int。2:用于引用计数的内存,管理附加此内存的 shared_ptr 对象的计数,最初计数将为1。

 

检查 shared_ptr 对象的引用计数

p1.use_count();

 

创建空的 shared_ptr 对象

因为带有参数的 shared_ptr 构造函数是 explicit 类型的,所以不能像这样std::shared_ptr<int> p1 = new int();隐式调用它构造函数。创建新的shared_ptr对象的最佳方法是使用std :: make_shared:

std::shared_ptr<int> p1 = std::make_shared<int>();

std::make_shared一次性为int对象和用于引用计数的数据都分配了内存,而new操作符只是为int分配了内存。

 

分离关联的原始指针

要使 shared_ptr 对象取消与相关指针的关联,可以使用reset()函数:

不带参数的reset():

p1.reset();

它将引用计数减少1,如果引用计数变为0,则删除指针。

带参数的reset():

p1.reset(newint(34));

在这种情况下,它将在内部指向新指针,因此其引用计数将再次变为1。

使用nullptr重置:

p1 =nullptr;

 

shared_ptr是一个伪指针

shared_ptr充当普通指针,我们可以将*->与 shared_ptr 对象一起使用,也可以像其他 shared_ptr 对象一样进行比较;

完整示例

#include <iostream>
#include  <memory> // 需要包含这个头文件

int main()
{
	// 使用 make_shared 创建空对象
	std::shared_ptr<int> p1 = std::make_shared<int>();
	*p1 = 78;
	std::cout << "p1 = " << *p1 << std::endl; // 输出78

	// 打印引用个数:1
	std::cout << "p1 Reference count = " << p1.use_count() << std::endl;

	// 第2个 shared_ptr 对象指向同一个指针
	std::shared_ptr<int> p2(p1);

	// 下面两个输出都是:2
	std::cout << "p2 Reference count = " << p2.use_count() << std::endl;
	std::cout << "p1 Reference count = " << p1.use_count() << std::endl;

	// 比较智能指针,p1 等于 p2
	if (p1 == p2) {
		std::cout << "p1 and p2 are pointing to same pointer\n";
	}

	std::cout<<"Reset p1 "<<std::endl;

	// 无参数调用reset,无关联指针,引用个数为0
	p1.reset();
	std::cout << "p1 Reference Count = " << p1.use_count() << std::endl;
	
	// 带参数调用reset,引用个数为1
	p1.reset(new int(11));
	std::cout << "p1  Reference Count = " << p1.use_count() << std::endl;

	// 把对象重置为NULL,引用计数为0
	p1 = nullptr;
	std::cout << "p1  Reference Count = " << p1.use_count() << std::endl;
	if (!p1) {
		std::cout << "p1 is NULL" << std::endl; // 输出
	}
	return 0;
}
 

2012 年的时候,SF 上也有人提问过类似的问题:

Is it possible to introduce Automatic Reference Counting (ARC) to C++?

个人觉得,如果语言加上 ARC 特性,那么也需要加上 AutoreleasePool 才能完美配合,而且内存管理模型也需要大改。

MyClass getMyClass() {
  // 这里要保证返回对象不析构,需要 AutoreleasePool 来让返回的对象延迟释放
  return MyClass();
}

但是 C++ 向后兼容的历史包袱导致不能增加这种特性。

这就导致想向增加或者借鉴某个语言的优点时,就显得非常困难。

最后只能造出智能指针这半自动的玩意。

真心希望 C++ 能大胆革新,果断在新标准中抛弃历史包袱。

posted on 2023-05-24 11:28  不及格的程序员-八神  阅读(39)  评论(0编辑  收藏  举报