1 似乎每个人在学习 iOS 过程中都考虑过的问题

  1. alloc retain release delloc 做了什么?

  2. autoreleasepool 是怎样实现的?

  3. __unsafe_unretained 是什么?

  4. Block 是怎样实现的

  5. 什么时候会引起循环引用,什么时候不会引起循环引用?

所以我将在本篇博文中详细的从 ARC 解释到 iOS 的内存管理,以及 Block 相关的原理、源码。

2 从 ARC 说起

说 iOS 的内存管理,就不得不从 ARC(Automatic Reference Counting / 自动引用计数) 说起, ARC 是 WWDC2011 和 iOS5 引入的变化。ARC 是 LLVM 3.0 编译器的特性,用来自动管理内存。

与 Java 中 GC 不同,ARC 是编译器特性,而不是基于运行时的,所以 ARC 其实是在编译阶段自动帮开发者插入了管理内存的代码,而不是实时监控与回收内存。

ARC 的内存管理规则可以简述为:

  1. 每个对象都有一个『被引用计数』

  2. 对象被持有,『被引用计数』+1

  3. 对象被放弃持有,『被引用计数』-1

  4. 『引用计数』=0,释放对象

3 你需要知道

  1. 包含 NSObject 类的 Foundation 框架并没有公开

  2. Core Foundation 框架源代码,以及通过 NSObject 进行内存管理的部分源代码是公开的。

  3. GNUstep 是 Foundation 框架的互换框架

GNUstep 也是 GNU 计划之一。将 Cocoa Objective-C 软件库以自由软件方式重新实现
某种意义上,GNUstep 和 Foundation 框架的实现是相似的
通过 GNUstep 的源码来分析 Foundation 的内存管理

4 alloc retain release dealloc 的实现

4.1 GNU – alloc

查看 GNUStep 中的 alloc 函数。

GNUstep/modules/core/base/Source/NSObject.m alloc:

+ (id) alloc

{

return [self allocWithZone: NSDefaultMallocZone()];

}

+ (id) allocWithZone: (NSZone*)z

{

return NSAllocateObject (self, 0, z);

}

GNUstep/modules/core/base/Source/NSObject.m NSAllocateObject:

struct obj_layout {

NSUInteger retained;

};

NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone)

{

int size = 计算容纳对象所需内存大小;

id new = NSZoneCalloc(zone, 1, size);

memset (new, 0, size);

new = (id)&((obj)new)[1];

}

NSAllocateObject 函数通过调用 NSZoneCalloc 函数来分配存放对象所需的空间,之后将该内存空间置为 nil,最后返回作为对象而使用的指针。

我们将上面的代码做简化整理:

GNUstep/modules/core/base/Source/NSObject.m alloc 简化版本:

struct obj_layout {

NSUInteger retained;

};

+ (id) alloc

{

int size = sizeof(struct obj_layout) + 对象大小;

struct obj_layout *p = (struct obj_layout *)calloc(1, size);

return (id)(p+1)

return [self allocWithZone: NSDefaultMallocZone()];

}

alloc 类方法用 struct obj_layout 中的 retained 整数来保存引用计数,并将其写入对象的内存头部,该对象内存块全部置为 0 后返回。

一个对象的表示便如下图:

4.2 GNU – retain

GNUstep/modules/core/base/Source/NSObject.m retainCount:

- (NSUInteger) retainCount

{

return NSExtraRefCount(self) + 1;

}

inline NSUInteger

NSExtraRefCount(id anObject)

{

return ((obj_layout)anObject)[-1].retained;

}

GNUstep/modules/core/base/Source/NSObject.m retain:

- (id) retain

{

NSIncrementExtraRefCount(self);

return self;

}

inline void

NSIncrementExtraRefCount(id anObject)

{

if (((obj)anObject)[-1].retained == UINT_MAX - 1)

[NSException raise: NSInternalInconsistencyException

format: @"NSIncrementExtraRefCount() asked to increment too far”];

((obj_layout)anObject)[-1].retained++;

}

以上代码中, NSIncrementExtraRefCount 方法首先写入了当 retained 变量超出最大值时发生异常的代码(因为 retained 是 NSUInteger 变量),然后进行 retain ++ 代码。

4.3 GNU – release

和 retain 相应的,release 方法做的就是 retain --。

GNUstep/modules/core/base/Source/NSObject.m release

- (oneway void) release

{

if (NSDecrementExtraRefCountWasZero(self))

{

[self dealloc];

}

}

BOOL

NSDecrementExtraRefCountWasZero(id anObject)

{

if (((obj)anObject)[-1].retained == 0)

{

return YES;

}

((obj)anObject)[-1].retained--;

return NO;

}

4.4 GNU – dealloc

dealloc 将会对对象进行释放。

GNUstep/modules/core/base/Source/NSObject.m dealloc:

- (void) dealloc

{

NSDeallocateObject (self);

}

inline void

NSDeallocateObject(id anObject)

{

obj_layout o = &((obj_layout)anObject)[-1];

free(o);

}

4.5 Apple 实现

在 Xcode 中 设置 Debug -> Debug Workflow -> Always Show Disassenbly 打开。这样在打断点后,可以看到更详细的方法调用。

通过在 NSObject 类的 alloc 等方法上设置断点追踪可以看到几个方法内部分别调用了:

retainCount

__CFdoExternRefOperation
CFBasicHashGetCountOfKey

retain

__CFdoExternRefOperation
CFBasicHashAddValue

release

__CFdoExternRefOperation
CFBasicHashRemoveValue

可以看到他们都调用了一个共同的 __CFdoExternRefOperation 方法。

该方法从前缀可以看到是包含在 Core Foundation,在 CFRuntime.c 中可以找到,做简化后列出源码:

CFRuntime.c __CFDoExternRefOperation:

int __CFDoExternRefOperation(uintptr_t op, id obj) {

CFBasicHashRef table = 取得对象的散列表(obj);

int count;

switch (op) {

case OPERATION_retainCount:

count = CFBasicHashGetCountOfKey(table, obj);

return count;

break;

case OPERATION_retain:

count = CFBasicHashAddValue(table, obj);

return obj;

case OPERATION_release:

count = CFBasicHashRemoveValue(table, obj);

return 0 == count;

}

}

所以 __CFDoExternRefOperation 是针对不同的操作,进行具体的方法调用,如果 op 是 OPERATION_retain,就去掉用具体实现 retain 的方法。

从 BasicHash 这样的方法名可以看出,其实引用计数表就是散列表。

key 为 hash(对象的地址) value 为 引用计数。

下图是 Apple 和 GNU 的实现对比:

5 autorelease 和 autorelaesepool

在苹果对于 NSAutoreleasePool 的文档中表示:

每个线程(包括主线程),都维护了一个管理 NSAutoreleasePool 的栈。当创先新的 Pool 时,他们会被添加到栈顶。当 Pool 被销毁时,他们会被从栈中移除。
autorelease 的对象会被添加到当前线程的栈顶的 Pool 中。当 Pool 被销毁,其中的对象也会被释放。
当线程结束时,所有的 Pool 被销毁释放。

对 NSAutoreleasePool 类方法和 autorelease 方法打断点,查看其运行过程,可以看到调用了以下函数:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

// 等同于 objc_autoreleasePoolPush

id obj = [[NSObject alloc] init];

[obj autorelease];

// 等同于 objc_autorelease(obj)

[NSAutoreleasePool showPools];

// 查看 NSAutoreleasePool 状况

[pool drain];

// 等同于 objc_autoreleasePoolPop(pool)

[NSAutoreleasePool showPools] 可以看到当前线程所有 pool 的情况:

objc[21536]: ##############

objc[21536]: AUTORELEASE POOLS for thread 0x10011e3c0

objc[21536]: 2 releases pending.

objc[21536]: [0x101802000] ................ PAGE (hot) (cold)

objc[21536]: [0x101802038] ################ POOL 0x101802038

objc[21536]: [0x101802040] 0x1003062e0 NSObject

objc[21536]: ##############

Program ended with exit code: 0

在 objc4 中可以查看到 AutoreleasePoolPage:

objc4/NSObject.mm AutoreleasePoolPage

class AutoreleasePoolPage

{

static inline void *push()

{

生成或者持有 NSAutoreleasePool 类对象

}

static inline void pop(void *token)

{

废弃 NSAutoreleasePool 类对象

releaseAll();

}

static inline id autorelease(id obj)

{

相当于 NSAutoreleasePool 类的 addObject 类方法

AutoreleasePoolPage *page = 取得正在使用的 AutoreleasePoolPage 实例;

}

id *add(id obj)

{

将对象追加到内部数组

}

void releaseAll()

{

调用内部数组中对象的 release 方法

}

};

void *

objc_autoreleasePoolPush(void)

{

if (UseGC) return nil;

return AutoreleasePoolPage::push();

}

void

objc_autoreleasePoolPop(void *ctxt)

{

if (UseGC) return;

AutoreleasePoolPage::pop(ctxt);

}

AutoreleasePoolPage 以双向链表的形式组合而成(分别对应结构中的 parent 指针和 child 指针)。

thread 指针指向当前线程。

每个 AutoreleasePoolPage 对象会开辟4096字节内存(也就是虚拟内存一页的大小),除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址。

next 指针指向下一个 add 进来的 autorelease 的对象即将存放的位置。

一个 Page 的空间被占满时,会新建一个 AutoreleasePoolPage 对象,连接链表。

6 __unsafe_unretained

有时候我们除了 __weak 和 __strong 之外也会用到 __unsafe_unretained 这个修饰符,那么我们对 __unsafe_unretained 了解多少?

__unsafe_unretained 是不安全的所有权修饰符,尽管 ARC 的内存管理是编译器的工作,但附有 __unsafe_unretained 修饰符的变量不属于编译器的内存管理对象。赋值时即不获得强引用也不获得弱引用。

来运行一段代码:

id __unsafe_unretained obj1 = nil;

{

id __strong obj0 = [[NSObject alloc] init];

obj1 = obj0;

NSLog(@"A: %@", obj1);

}

NSLog(@"B: %@", obj1);

运行结果:

2017-01-12 19:24:47.245220 __unsafe_unretained[55726:4408416] A:

2017-01-12 19:24:47.246670 __unsafe_unretained[55726:4408416] B:

Program ended with exit code: 0

对代码进行详细分析:

id __unsafe_unretained obj1 = nil;

{

// 自己生成并持有对象

id __strong obj0 = [[NSObject alloc] init];

// 因为 obj0 变量为强引用,

// 所以自己持有对象

obj1 = obj0;

// 虽然 obj0 变量赋值给 obj1

// 但是 obj1 变量既不持有对象的强引用,也不持有对象的弱引用

NSLog(@"A: %@", obj1);

// 输出 obj1 变量所表示的对象

}

NSLog(@"B: %@", obj1);

// 输出 obj1 变量所表示的对象

// obj1 变量表示的对象已经被废弃

// 所以此时获得的是悬垂指针

// 错误访问

所以,最后的 NSLog 只是碰巧正常运行,如果错误访问,会造成 crash

在使用 __unsafe_unretained 修饰符时,赋值给附有 __strong 修饰符变量时,要确保对象确实存在

1 似乎每个人在学习 iOS 过程中都考虑过的问题

  1. alloc retain release delloc 做了什么?

  2. autoreleasepool 是怎样实现的?

  3. __unsafe_unretained 是什么?

  4. Block 是怎样实现的

  5. 什么时候会引起循环引用,什么时候不会引起循环引用?

所以我将在本篇博文中详细的从 ARC 解释到 iOS 的内存管理,以及 Block 相关的原理、源码。

2 从 ARC 说起

说 iOS 的内存管理,就不得不从 ARC(Automatic Reference Counting / 自动引用计数) 说起, ARC 是 WWDC2011 和 iOS5 引入的变化。ARC 是 LLVM 3.0 编译器的特性,用来自动管理内存。

与 Java 中 GC 不同,ARC 是编译器特性,而不是基于运行时的,所以 ARC 其实是在编译阶段自动帮开发者插入了管理内存的代码,而不是实时监控与回收内存。

ARC 的内存管理规则可以简述为:

  1. 每个对象都有一个『被引用计数』

  2. 对象被持有,『被引用计数』+1

  3. 对象被放弃持有,『被引用计数』-1

  4. 『引用计数』=0,释放对象

3 你需要知道

  1. 包含 NSObject 类的 Foundation 框架并没有公开

  2. Core Foundation 框架源代码,以及通过 NSObject 进行内存管理的部分源代码是公开的。

  3. GNUstep 是 Foundation 框架的互换框架

GNUstep 也是 GNU 计划之一。将 Cocoa Objective-C 软件库以自由软件方式重新实现
某种意义上,GNUstep 和 Foundation 框架的实现是相似的
通过 GNUstep 的源码来分析 Foundation 的内存管理

4 alloc retain release dealloc 的实现

4.1 GNU – alloc

查看 GNUStep 中的 alloc 函数。

GNUstep/modules/core/base/Source/NSObject.m alloc:

+ (id) alloc

{

return [self allocWithZone: NSDefaultMallocZone()];

}

+ (id) allocWithZone: (NSZone*)z

{

return NSAllocateObject (self, 0, z);

}

GNUstep/modules/core/base/Source/NSObject.m NSAllocateObject:

struct obj_layout {

NSUInteger retained;

};

NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone)

{

int size = 计算容纳对象所需内存大小;

id new = NSZoneCalloc(zone, 1, size);

memset (new, 0, size);

new = (id)&((obj)new)[1];

}

NSAllocateObject 函数通过调用 NSZoneCalloc 函数来分配存放对象所需的空间,之后将该内存空间置为 nil,最后返回作为对象而使用的指针。

我们将上面的代码做简化整理:

GNUstep/modules/core/base/Source/NSObject.m alloc 简化版本:

struct obj_layout {

NSUInteger retained;

};

+ (id) alloc

{

int size = sizeof(struct obj_layout) + 对象大小;

struct obj_layout *p = (struct obj_layout *)calloc(1, size);

return (id)(p+1)

return [self allocWithZone: NSDefaultMallocZone()];

}

alloc 类方法用 struct obj_layout 中的 retained 整数来保存引用计数,并将其写入对象的内存头部,该对象内存块全部置为 0 后返回。

一个对象的表示便如下图:

4.2 GNU – retain

GNUstep/modules/core/base/Source/NSObject.m retainCount:

- (NSUInteger) retainCount

{

return NSExtraRefCount(self) + 1;

}

inline NSUInteger

NSExtraRefCount(id anObject)

{

return ((obj_layout)anObject)[-1].retained;

}

GNUstep/modules/core/base/Source/NSObject.m retain:

- (id) retain

{

NSIncrementExtraRefCount(self);

return self;

}

inline void

NSIncrementExtraRefCount(id anObject)

{

if (((obj)anObject)[-1].retained == UINT_MAX - 1)

[NSException raise: NSInternalInconsistencyException

format: @"NSIncrementExtraRefCount() asked to increment too far”];

((obj_layout)anObject)[-1].retained++;

}

以上代码中, NSIncrementExtraRefCount 方法首先写入了当 retained 变量超出最大值时发生异常的代码(因为 retained 是 NSUInteger 变量),然后进行 retain ++ 代码。

4.3 GNU – release

和 retain 相应的,release 方法做的就是 retain --。

GNUstep/modules/core/base/Source/NSObject.m release

- (oneway void) release

{

if (NSDecrementExtraRefCountWasZero(self))

{

[self dealloc];

}

}

BOOL

NSDecrementExtraRefCountWasZero(id anObject)

{

if (((obj)anObject)[-1].retained == 0)

{

return YES;

}

((obj)anObject)[-1].retained--;

return NO;

}

4.4 GNU – dealloc

dealloc 将会对对象进行释放。

GNUstep/modules/core/base/Source/NSObject.m dealloc:

- (void) dealloc

{

NSDeallocateObject (self);

}

inline void

NSDeallocateObject(id anObject)

{

obj_layout o = &((obj_layout)anObject)[-1];

free(o);

}

4.5 Apple 实现

在 Xcode 中 设置 Debug -> Debug Workflow -> Always Show Disassenbly 打开。这样在打断点后,可以看到更详细的方法调用。

通过在 NSObject 类的 alloc 等方法上设置断点追踪可以看到几个方法内部分别调用了:

retainCount

__CFdoExternRefOperation
CFBasicHashGetCountOfKey

retain

__CFdoExternRefOperation
CFBasicHashAddValue

release

__CFdoExternRefOperation
CFBasicHashRemoveValue

可以看到他们都调用了一个共同的 __CFdoExternRefOperation 方法。

该方法从前缀可以看到是包含在 Core Foundation,在 CFRuntime.c 中可以找到,做简化后列出源码:

CFRuntime.c __CFDoExternRefOperation:

int __CFDoExternRefOperation(uintptr_t op, id obj) {

CFBasicHashRef table = 取得对象的散列表(obj);

int count;

switch (op) {

case OPERATION_retainCount:

count = CFBasicHashGetCountOfKey(table, obj);

return count;

break;

case OPERATION_retain:

count = CFBasicHashAddValue(table, obj);

return obj;

case OPERATION_release:

count = CFBasicHashRemoveValue(table, obj);

return 0 == count;

}

}

所以 __CFDoExternRefOperation 是针对不同的操作,进行具体的方法调用,如果 op 是 OPERATION_retain,就去掉用具体实现 retain 的方法。

从 BasicHash 这样的方法名可以看出,其实引用计数表就是散列表。

key 为 hash(对象的地址) value 为 引用计数。

下图是 Apple 和 GNU 的实现对比:

5 autorelease 和 autorelaesepool

在苹果对于 NSAutoreleasePool 的文档中表示:

每个线程(包括主线程),都维护了一个管理 NSAutoreleasePool 的栈。当创先新的 Pool 时,他们会被添加到栈顶。当 Pool 被销毁时,他们会被从栈中移除。
autorelease 的对象会被添加到当前线程的栈顶的 Pool 中。当 Pool 被销毁,其中的对象也会被释放。
当线程结束时,所有的 Pool 被销毁释放。

对 NSAutoreleasePool 类方法和 autorelease 方法打断点,查看其运行过程,可以看到调用了以下函数:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

// 等同于 objc_autoreleasePoolPush

id obj = [[NSObject alloc] init];

[obj autorelease];

// 等同于 objc_autorelease(obj)

[NSAutoreleasePool showPools];

// 查看 NSAutoreleasePool 状况

[pool drain];

// 等同于 objc_autoreleasePoolPop(pool)

[NSAutoreleasePool showPools] 可以看到当前线程所有 pool 的情况:

objc[21536]: ##############

objc[21536]: AUTORELEASE POOLS for thread 0x10011e3c0

objc[21536]: 2 releases pending.

objc[21536]: [0x101802000] ................ PAGE (hot) (cold)

objc[21536]: [0x101802038] ################ POOL 0x101802038

objc[21536]: [0x101802040] 0x1003062e0 NSObject

objc[21536]: ##############

Program ended with exit code: 0

在 objc4 中可以查看到 AutoreleasePoolPage:

objc4/NSObject.mm AutoreleasePoolPage

class AutoreleasePoolPage

{

static inline void *push()

{

生成或者持有 NSAutoreleasePool 类对象

}

static inline void pop(void *token)

{

废弃 NSAutoreleasePool 类对象

releaseAll();

}

static inline id autorelease(id obj)

{

相当于 NSAutoreleasePool 类的 addObject 类方法

AutoreleasePoolPage *page = 取得正在使用的 AutoreleasePoolPage 实例;

}

id *add(id obj)

{

将对象追加到内部数组

}

void releaseAll()

{

调用内部数组中对象的 release 方法

}

};

void *

objc_autoreleasePoolPush(void)

{

if (UseGC) return nil;

return AutoreleasePoolPage::push();

}

void

objc_autoreleasePoolPop(void *ctxt)

{

if (UseGC) return;

AutoreleasePoolPage::pop(ctxt);

}

AutoreleasePoolPage 以双向链表的形式组合而成(分别对应结构中的 parent 指针和 child 指针)。

thread 指针指向当前线程。

每个 AutoreleasePoolPage 对象会开辟4096字节内存(也就是虚拟内存一页的大小),除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址。

next 指针指向下一个 add 进来的 autorelease 的对象即将存放的位置。

一个 Page 的空间被占满时,会新建一个 AutoreleasePoolPage 对象,连接链表。

6 __unsafe_unretained

有时候我们除了 __weak 和 __strong 之外也会用到 __unsafe_unretained 这个修饰符,那么我们对 __unsafe_unretained 了解多少?

__unsafe_unretained 是不安全的所有权修饰符,尽管 ARC 的内存管理是编译器的工作,但附有 __unsafe_unretained 修饰符的变量不属于编译器的内存管理对象。赋值时即不获得强引用也不获得弱引用。

来运行一段代码:

id __unsafe_unretained obj1 = nil;

{

id __strong obj0 = [[NSObject alloc] init];

obj1 = obj0;

NSLog(@"A: %@", obj1);

}

NSLog(@"B: %@", obj1);

运行结果:

2017-01-12 19:24:47.245220 __unsafe_unretained[55726:4408416] A:

2017-01-12 19:24:47.246670 __unsafe_unretained[55726:4408416] B:

Program ended with exit code: 0

对代码进行详细分析:

id __unsafe_unretained obj1 = nil;

{

// 自己生成并持有对象

id __strong obj0 = [[NSObject alloc] init];

// 因为 obj0 变量为强引用,

// 所以自己持有对象

obj1 = obj0;

// 虽然 obj0 变量赋值给 obj1

// 但是 obj1 变量既不持有对象的强引用,也不持有对象的弱引用

NSLog(@"A: %@", obj1);

// 输出 obj1 变量所表示的对象

}

NSLog(@"B: %@", obj1);

// 输出 obj1 变量所表示的对象

// obj1 变量表示的对象已经被废弃

// 所以此时获得的是悬垂指针

// 错误访问

所以,最后的 NSLog 只是碰巧正常运行,如果错误访问,会造成 crash

在使用 __unsafe_unretained 修饰符时,赋值给附有 __strong 修饰符变量时,要确保对象确实存在

iOS 进阶—— iOS 内存管理的更多相关文章

  1. iOS夯实:内存管理

    iOS夯实:内存管理 文章转自 内存管理 最近的学习计划是将iOS的机制原理好好重新打磨学习一下,总结和加入自己的思考. 有不正确的地方,多多指正. 目录: 基本信息 旧时代的细节 新时代 基本信息 ...

  2. IOS开发的内存管理

    关于IOS开发的内存管理的文章已经很多了,因此系统的知识点就不写了,这里我写点平时工作遇到的疑问以及解答做个总结吧,相信也会有人遇到相同的疑问呢,欢迎学习IOS的朋友请加ios技术交流群:190956 ...

  3. IOS开发小记-内存管理

    关于IOS开发的内存管理的文章已经很多了,因此系统的知识点就不写了,这里我写点平时工作遇到的疑问以及解答做个总结吧,相信也会有人遇到相同的疑问呢,欢迎学习IOS的朋友请加ios技术交流群:190956 ...

  4. [转载]对iOS开发中内存管理的一点总结与理解

    对iOS开发中内存管理的一点总结与理解   做iOS开发也已经有两年的时间,觉得有必要沉下心去整理一些东西了,特别是一些基础的东西,虽然现在有ARC这种东西,但是我一直也没有去用过,个人觉得对内存操作 ...

  5. iOS开发ARC内存管理技术要点

    本文来源于我个人的ARC学习笔记,旨在通过简明扼要的方式总结出iOS开发中ARC(Automatic Reference Counting,自动引用计数)内存管理技术的要点,所以不会涉及全部细节.这篇 ...

  6. iOS MRC ARC 内存管理

    转自:http://www.jianshu.com/p/48665652e4e4 1. 什么是内存管理 程序在运行的过程中通常通过以下行为,来增加程序的的内存占用 创建一个OC对象 定义一个变量 调用 ...

  7. iOS开发ARC内存管理

    本文的主要内容: ARC的本质 ARC的开启与关闭 ARC的修饰符 ARC与Block ARC与Toll-Free Bridging ARC的本质 ARC是编译器(时)特性,而不是运行时特性,更不是垃 ...

  8. iOS另类的内存管理

    iOS的内存管理算是老生常谈的问题了,我们写iOS的时候无时无刻不在涉及到内存管理.从开始的MRR(manual retain-release)到后来ARC(Automatic Reference C ...

  9. IOS开发之内存管理--dealloc该写些什么

    在非ARC开发环境中,dealloc是类释放前,清理内存的最后机会.到底那些变量和属性该释放呢,一些特殊的类(nstimer,observer)该怎么释放.需要注意的是不释放会引起内存泄露,过度释放也 ...

随机推荐

  1. Gitlab自动触发Jenkins构建打包

    一.目的 在部门的测试环境中,开发人员一旦向gitlab仓库提交成功代码,gitlab就会自动触发jenkins构建项目.当然在构建后还可以添加项目部署或者自动化测试的脚本.这里只针对测试环境. 二. ...

  2. 大神博客链接系列---C#SubSonic3.0搭建ORM

    一.C#框架 C#ORM框架: SubSonic3.0制作ORM--- http://www.cnblogs.com/EmptyFS/p/3659679.html

  3. C# tostring

    GUID: 即Globally Unique Identifier(全球唯一标识符) 也称作 UUID(Universally Unique IDentifier) . GUID是一个通过特定算法产生 ...

  4. A workaround to change shared memory size for Docker containers in AWS ECS

    Issue Because of not supporting to specify the following docker run parameter, containers in ECS can ...

  5. win10下Anaconda 2 和 3 共存安装,并切换jupyter notebook和Pycharm中的对应版本

    win10下Anaconda 2 和 3 共存安装,并切换jupyter notebook和Pycharm中的对应版本 zoerywzhou@163.com http://www.cnblogs.co ...

  6. [C#]使用Gembox.SpreadSheet向Excel写入数据及图表

    本文为原创文章.源代码为原创代码,如转载/复制,请在网页/代码处明显位置标明原文名称.作者及网址,谢谢! 开发工具:VS2017 语言:C# DotNet版本:.Net FrameWork 4.0及以 ...

  7. 使用map做数组与链表去重

    #include<iostream> #include<map> using namespace std; class node{ public: node():value() ...

  8. liunx中图形化桌面环境中的脚本编程之——创建文本菜单

    整体是通过建立菜单布局,然后根据菜单上的布局建立函数(就是为这些布局实现功能),最后将函数和布局实现逻辑上的相连: 如何建立菜单布局? 整体上是通过打印字符创建出,因此需要使用echo命令,但是有一些 ...

  9. Angular4.0引入laydate.js日期插件方法

    Angular是不支持直接引入js文件的,下面介绍项目如果引入laydate.js的方法 一.将下载的laydate中的js和theme文件放到一个统一的文件下面,我把它放到asset下 二.在ang ...

  10. FreeRTOS--疑难解答

    此章节涉及新手最常遇见的3种问题: 错误的中断优先级设置 栈溢出 不恰当的使用printf() 使用configASSERT()能够显著地提高生产效率,它能够捕获.识别多种类型的错误.强烈建议在开发或 ...