iOS 进阶—— iOS 内存管理
1 似乎每个人在学习 iOS 过程中都考虑过的问题
alloc retain release delloc 做了什么?
autoreleasepool 是怎样实现的?
__unsafe_unretained 是什么?
Block 是怎样实现的
什么时候会引起循环引用,什么时候不会引起循环引用?
所以我将在本篇博文中详细的从 ARC 解释到 iOS 的内存管理,以及 Block 相关的原理、源码。
2 从 ARC 说起
说 iOS 的内存管理,就不得不从 ARC(Automatic Reference Counting / 自动引用计数) 说起, ARC 是 WWDC2011 和 iOS5 引入的变化。ARC 是 LLVM 3.0 编译器的特性,用来自动管理内存。
与 Java 中 GC 不同,ARC 是编译器特性,而不是基于运行时的,所以 ARC 其实是在编译阶段自动帮开发者插入了管理内存的代码,而不是实时监控与回收内存。
ARC 的内存管理规则可以简述为:
每个对象都有一个『被引用计数』
对象被持有,『被引用计数』+1
对象被放弃持有,『被引用计数』-1
『引用计数』=0,释放对象
3 你需要知道
包含 NSObject 类的 Foundation 框架并没有公开
Core Foundation 框架源代码,以及通过 NSObject 进行内存管理的部分源代码是公开的。
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 过程中都考虑过的问题
alloc retain release delloc 做了什么?
autoreleasepool 是怎样实现的?
__unsafe_unretained 是什么?
Block 是怎样实现的
什么时候会引起循环引用,什么时候不会引起循环引用?
所以我将在本篇博文中详细的从 ARC 解释到 iOS 的内存管理,以及 Block 相关的原理、源码。
2 从 ARC 说起
说 iOS 的内存管理,就不得不从 ARC(Automatic Reference Counting / 自动引用计数) 说起, ARC 是 WWDC2011 和 iOS5 引入的变化。ARC 是 LLVM 3.0 编译器的特性,用来自动管理内存。
与 Java 中 GC 不同,ARC 是编译器特性,而不是基于运行时的,所以 ARC 其实是在编译阶段自动帮开发者插入了管理内存的代码,而不是实时监控与回收内存。
ARC 的内存管理规则可以简述为:
每个对象都有一个『被引用计数』
对象被持有,『被引用计数』+1
对象被放弃持有,『被引用计数』-1
『引用计数』=0,释放对象
3 你需要知道
包含 NSObject 类的 Foundation 框架并没有公开
Core Foundation 框架源代码,以及通过 NSObject 进行内存管理的部分源代码是公开的。
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 内存管理的更多相关文章
- iOS夯实:内存管理
iOS夯实:内存管理 文章转自 内存管理 最近的学习计划是将iOS的机制原理好好重新打磨学习一下,总结和加入自己的思考. 有不正确的地方,多多指正. 目录: 基本信息 旧时代的细节 新时代 基本信息 ...
- IOS开发的内存管理
关于IOS开发的内存管理的文章已经很多了,因此系统的知识点就不写了,这里我写点平时工作遇到的疑问以及解答做个总结吧,相信也会有人遇到相同的疑问呢,欢迎学习IOS的朋友请加ios技术交流群:190956 ...
- IOS开发小记-内存管理
关于IOS开发的内存管理的文章已经很多了,因此系统的知识点就不写了,这里我写点平时工作遇到的疑问以及解答做个总结吧,相信也会有人遇到相同的疑问呢,欢迎学习IOS的朋友请加ios技术交流群:190956 ...
- [转载]对iOS开发中内存管理的一点总结与理解
对iOS开发中内存管理的一点总结与理解 做iOS开发也已经有两年的时间,觉得有必要沉下心去整理一些东西了,特别是一些基础的东西,虽然现在有ARC这种东西,但是我一直也没有去用过,个人觉得对内存操作 ...
- iOS开发ARC内存管理技术要点
本文来源于我个人的ARC学习笔记,旨在通过简明扼要的方式总结出iOS开发中ARC(Automatic Reference Counting,自动引用计数)内存管理技术的要点,所以不会涉及全部细节.这篇 ...
- iOS MRC ARC 内存管理
转自:http://www.jianshu.com/p/48665652e4e4 1. 什么是内存管理 程序在运行的过程中通常通过以下行为,来增加程序的的内存占用 创建一个OC对象 定义一个变量 调用 ...
- iOS开发ARC内存管理
本文的主要内容: ARC的本质 ARC的开启与关闭 ARC的修饰符 ARC与Block ARC与Toll-Free Bridging ARC的本质 ARC是编译器(时)特性,而不是运行时特性,更不是垃 ...
- iOS另类的内存管理
iOS的内存管理算是老生常谈的问题了,我们写iOS的时候无时无刻不在涉及到内存管理.从开始的MRR(manual retain-release)到后来ARC(Automatic Reference C ...
- IOS开发之内存管理--dealloc该写些什么
在非ARC开发环境中,dealloc是类释放前,清理内存的最后机会.到底那些变量和属性该释放呢,一些特殊的类(nstimer,observer)该怎么释放.需要注意的是不释放会引起内存泄露,过度释放也 ...
随机推荐
- Lua API 小记1
这些东西是平时遇到的, 觉得有一定的价值, 所以记录下来, 以后遇到类似的问题可以查阅, 同时分享出来也能方便需要的人, 转载请注明来自RingOfTheC[ring.of.the.c@gmail.c ...
- HTML5到底将给企业带来什么?
HTML5 是近年来互联网行业的热门词汇,火的很.有人高调宣称"APP 将在几年内灭亡,HTML5 取而代之" 改变企业网络广告的模式与分布 广告是企业网络营销的主要方式之一 十几 ...
- php代码中的细节问题
本次主要谈及工作中关于注销功能中的路径设置及session的清除问题之前的调试一直忽略了session的功能,以至于每次使用__ROOT__/index.php/home/Student/logout ...
- springboot mybatis redis 二级缓存
前言 什么是mybatis二级缓存? 二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace. 即,在不同的sqlsession中,相同的namespace下,相同 ...
- window下phpstudy的nginx配置虚拟主机
由于很长时间没有配置Apache,虽然说知道怎么配置nginx,但是还是花费了一些时间这次记下来下次直接用 在其他选项文件菜单中->打开配置文件->选择vhosts-conf nginx的 ...
- 一些内存模型、并发、netty知识点的记录
happens-before:描述内存可见性as-if-serial:无论怎么重排序,程序的运行结果不会改变 ReentrantLock依赖了队列同步器AQS,其实现方式是volatile变量的读写操 ...
- C#访问C++动态分配的数组指针
项目中遇到C#调用C++算法库的情况,C++内部运算结果返回矩形坐标数组(事先长度未知且不可预计),下面方法适用于访问C++内部分配的任何结构体类型数组.当时想当然的用ref array[]传递参数, ...
- SQL Server学习之路(二):主键和外键
0.目录 1.定义 1.1 什么是主键和外键 1.2 主键和外键的作用 1.3 主键.外键和索引的区别 2.主键(primary key) 2.1 通过SSMS设置主键 2.2 通过SQL语句设置主键 ...
- 基于‘BOSS直聘的招聘信息’分析企业到底需要什么样的PHP程序员
原文地址:http://www.jtahstu.com/blog/scrapy_zhipin_php.html 基于'BOSS直聘的招聘信息'分析企业到底需要什么样的PHP程序员 标签(空格分隔): ...
- 《算法》C/C++ 图形处理
概述 一般图形输出无法就是用循环输出 ,提前构造好图形. 两种方式 ** 模拟法直接输出** ** 二维数组 构造输出** 问题描述 利用字母可以组成一些美丽的图形,下面给出了一个例子: ABCDEF ...