iOS 学习日志(3)--关于Automatic Reference Counting

一、关于Alloc Retain Release 等

iOS 学习日志(3)--关于Automatic Reference Counting_第1张图片

Aciton for Object-C Object
Objective-C Method
Create and have ownership of it alloc/new/copy/mutableCopy group
Take ownership of it retain
Relinquish it  release
Dispose of it dealloc

alloc:创建一个对象,并拥有这个对象的使用权利。The implementation of alloc in NSObject:

关于NSZone:						
是苹果对内存分配和释放的优化方式。NSZone不是一个对象;它是一个难懂的C结构,它被用于纪录关于内存处理(管理)一系列对象的信息。
    你几乎不需要担忧你自己的应用(applications)是怎样管理你自己的空间(zones)的 ;Cocoa透明地管理它。默认的NSZone在程序启动和所有对象被分配时创建。
    如果你大量分配数百个小对象,事实上你会发现你花费精力来为他们分配内存是有意义的。因为这种标准的(默认的)空间会被一直使用,它会变得斑驳起来;释放对象的过程会给整个内存留下令人尴尬的空隙。标准空间的分配器(allocator)也知道知道这一点,所以它尝试着优先去使用被用户释放的内存,去填补这些空隙,但是这种方式只有在空间(zone) 变得很大时才有明显效果。

使用NSZone
+ (id) alloc
{return [self allocWithZone: NSDefaultMallocZone()];
}
+ (id) allocWithZone: (NSZone*)z
{return NSAllocateObject (self, 0, z);
} 
				
struct obj_layout {				
NSUInteger retained;
};					
inline id
NSAllocateObject (Class aClass, NSUInteger extraBytes, NSZone *zone)			
{
int size = /* needed size to store the object */
id new = NSZoneMalloc(zone, size); // 调用NSZoneMalloc 开辟一块内存空间,返回内存地址
memset(new, 0, size);
new = (id)&((struct obj_layout *)new)[1];				
} 

去掉NSZone:					
struct obj_layout {		
NSUInteger retained;
};				
+ (id) alloc				
{
int size = sizeof(struct obj_layout) + size_of_the_object;
struct obj_layout *p = (struct obj_layout *)calloc(1, size);
return (id)(p + 1);				
}	

retian:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为 1。指针的拷贝

- (id) retain
{
    NSIncrementExtraRefCount(self);
    return self;
}
inline void
NSIncrementExtraRefCount(id anObject)
{
   if (((struct obj_layout *)anObject)[-1].retained == UINT_MAX - 1)
   [NSException raise: NSInternalInconsistencyExceptionformat: @"NSIncrementExtraRefCount() asked to increment too far"];
   ((struct obj_layout *)anObject)[-1].retained++;
}

release:通知内存释放这个对象,只有release才会真正释放内存。

- (void) release
{if (NSDecrementExtraRefCountWasZero(self))
[self dealloc];
}
BOOLNSDecrementExtraRefCountWasZero(id anObject)
{if (((struct obj_layout *)anObject)[-1].retained == 0) {
        return YES;
    } else {
((struct obj_layout *)anObject)[-1].retained--;
return NO;
}
}

nil:是把一个对象的指针置为空,切断了指针与内存中对象的联系;

release与nil 使用的先后顺序:

如果没有release就直接nil,那么虽然不会出错,却等于自己制造内存泄漏了,因为nil之后release就已经不起作用了。相反,如果在使用接口对象时只仅仅release没有设置self.objc =nil,那么程序可能也不会报错,但却会十分不稳定、不健壮,很容易发生崩溃现象。因为一个接口对象在release之后,给它所分配等内存就已经被释放了,如果释放之后系统再用到这个对象,那么程序就会crash。如果释放之后把它的指针置为空,则即便后面的程序用到该对象,也不会崩溃。

dealloc:众所周知,dealloc是非ARC情况下,调用dealloc是释放内存的。ARC环境下,也没有把dealloc函数禁掉,还是可以使用的,只不过不用调用[super dealloc]了。例如:页面中调用webview。

如果在WebView载入完成之前关闭画面的话,画面关闭后,ViewController也释放了。但由于WebView正在载入页面,而不会马上被释放,等到页面载入完毕后,回调delegate(ViewController)中的方法,由于此时ViewController已经被释放,所以会出错。

解决办法是在dealloc中把WebView的delegate释放。

-(void)dealloc {

    self.webView.delegate = nil;

}

附录:

1、关于self的用法 找到一篇博客写的不错(http://blog.csdn.net/zhibudefeng/article/details/7714808)

主要是关于OC里面的getter、setter有关。self.object 与 myClass -> myObject来访问, 这样是直接访问对象本身

2、关于声明成IBOutlet属性:

在MRC中,IBOutlet本身就是retain 任何一个被声明为IBOutlet并且在Interface Builder里被连接到一个UI组件的成员变量,会被额外retain一次。所以使用了IBOutlet变量,一定要在dealloc/viewDidUnload里释放这个变量

在ARC中,我们使用IBOutlet属性都声明为weak。通过加载xib得到的用户界面,在其从xib文件加载时,就已经是view hierarchy的一部分了,而view hierarchy中的指向都是strong的。因此outlet所指向的UI对象不应当再被hold一次了

二、NSAutoreleasePool 的使用:

iOS 学习日志(3)--关于Automatic Reference Counting_第2张图片

Apple's implementtation of autorelease in runtime/objc-arr.mm

class AutoreleasePoolPage
{static inline void *push()
{/* It corresponds to creation and ownership of an NSAutoreleasePool object */
}
static inline void pop(void *token)
{/* It corresponds to disposal of an NSAutoreleasePool object */
releaseAll();
}
static inline id autorelease(id obj)
{/* It corresponds to NSAutoreleasePool class method addObject. */
AutoreleasePoolPage *autoreleasePoolPage = /* getting active AutoreleasePoolPage
object */
autoreleasePoolPage->add(obj);
}
id *add(id obj)
{/* add the obj to an internal array; */
}
void releaseAll()
{/* calls release for all the objects in the internal array */
}
};
void *objc_autoreleasePoolPush(void)
{return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt)
{AutoreleasePoolPage::pop(ctxt);
}
id objc_autorelease(id obj)
{return AutoreleasePoolPage::autorelease(obj);
}

例如:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // equivalent to objc_autoreleasePoolPush()				
id obj = [[NSObject alloc] init];				
[obj autorelease]; // equivalent to objc_autorelease(obj)
[pool drain]; // equivalent to objc_autoreleasePoolPop(pool)

当drain 被调用的时候,会调用dealloc函数来释放pool的数组,在之前会先释放pool数组里面的所有object

-(void) drain {
   [self dealloc];
}

-(void) dealloc {
  [self emptyPool];
  [array release];
}

-(void) emptyPool {
  for(id objc in array) {
     [objc release];
  }
}

在ARC与非ARC中,autoreleasepool的实现方式:

iOS 学习日志(3)--关于Automatic Reference Counting_第3张图片

离开@autoreleasepool 的块,所有创建的对象都会自动释放

你可能感兴趣的:(iOS 学习日志(3)--关于Automatic Reference Counting)