MRC內存管理原則
1. You have ownership of any objects you create.
2. You can take ownership of an object using retain.
3. When no longer needed, you must relinquish ownership of an object you own.
4. You must not relinquish ownership of an object you don’t own.
Action for Objective-C Object | Objective-C Method |
---|---|
Create and have ownership of it | alloc/new/copy/mutableCopy group |
Take ownership of it | retian |
Relinquish it | release |
Dispose of it | dealloc |
調用者持有
Relinquishing Ownership of a Retained Object. 下面的例子將函數將自己持有的obj, 通過return傳遞給了調用者,由調用者來來持有此變量
- (id)allocObject { /* - You create an object and have ownership. */ id obj = [[NSObject alloc] init]; /* - At this moment, this method has ownership of the object. */ return obj; }
調用者不持有
通過autorelease, 可以讓調用者不持有該對象. Autorelease offers a mechanism to relinquish objects properly when the lifetime of the objects has ended.自動釋放提供了一種, 當對象生命週期結束時,自動釋放的機制.
- (id)object { id obj = [[NSObject alloc] init]; /* - At this moment, this method has ownership of the object. */ [obj autorelease]; /* - The object exists, and you don’t have ownership of it. */ return obj; }
autorelease
通過autorelease將對象註冊到 最近的autoReleasePool中,當pool調用drain
時, 該對象的release被調用.
不要釋放, 非你持有的對象
app crash
id obj = [[NSObject alloc] init]; [obj release]; [obj release];
id obj1 = [obj0 object]; [obj1 release];
GNUstep — The alloc Method
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); }
動態分配地址, 並將第一個字節, 轉換爲引用計數.
GNUstep — malloc && calloc
malloc 分配的空間沒有初始化爲0, calloc分配的空間初始化爲0; calloc可以分配多個
GNUstep — The retain Method
The alloc method returns a memory block filled with zero containing a struct obj_layout header, which has a variable “retained” to store the number of references. This number is called the reference count
id obj = [[NSObject alloc] init]; NSLog(@"retainCount=%d", [obj retainCount]); //1
- (NSUInteger) retainCount { return NSExtraRefCount(self) + 1; } inline NSUInteger NSExtraRefCount(id anObject){ return ((struct obj_layout *)anObject)[-1].retained; }
- (id) retain { NSIncrementExtraRefCount(self); return self; } inline void NSIncrementExtraRefCount(id anObject) { if (((struct obj_layout *)anObject)[-1].retained == UINT_MAX - 1) [NSException raise: NSInternalInconsistencyException format: @"NSIncrementExtraRefCount() asked to increment too far"]; ((struct obj_layout *)anObject)[-1].retained++; }
GNUstep — The release Method
- (void) release { if (NSDecrementExtraRefCountWasZero(self)) [self dealloc]; } BOOL NSDecrementExtraRefCountWasZero(id anObject) { if (((struct obj_layout *)anObject)[-1].retained == 0) { return YES; } else { ((struct obj_layout *)anObject)[-1].retained--; return NO; } }
GNUstep — The dealloc Method
- (void) dealloc NSDeallocateObject (self); } inline void NSDeallocateObject(id anObject) { struct obj_layout *o = &((struct obj_layout *)anObject)[-1]; free(o); }
GNUstep — 總結
1. All Objective-C objects have an integer value called the reference count.
2. The reference count is incremented by one when one of alloc/new/copy/mutableCopy or retain is called.
3. It is decremented by one when release is called.
4. Dealloc is called when the integer counter becomes zero.
Apple’s Implementation
-retainCount __CFDoExternRefOperation CFBasicHashGetCountOfKey -retain __CFDoExternRefOperation CFBasicHashAddValue -release __CFDoExternRefOperation CFBasicHashRemoveValue
int __CFDoExternRefOperation(uintptr_t op, id obj) { CFBasicHashRef table = get hashtable from obj; int count; switch (op) { case OPERATION_retainCount: count = CFBasicHashGetCountOfKey(table, obj); return count; case OPERATION_retain: CFBasicHashAddValue(table, obj); return obj; case OPERATION_release: count = CFBasicHashRemoveValue(table, obj); return 0 == count; } }
在GUNstep的實現中, 引用計數存放在每個obj的header部分, 而Apple的實現, 將所有的對象的引用計數存放在一個HashTable中,
GUNstep實現的好處:
- 更少的代碼
- 很容易控制生命週期, 因爲計數, 本身包含在每個對象中
Apple實現的好處:
- 每個對象不必有header部分, 從而不需要考慮對齊的問題
- 計數統一管理, 每個對象的計數都可以訪問到, 便於系統批量處理.
- 當每個對象的內存出問題的時候, 可以通過hashTable來定位到該對象
Autorelease
Automatic Variables
當一個自動變量, 超出其作用域時, 會自動釋放.
{ int a; }
With autorelease, you can use objects in the same manner as automatic variables, meaning that when execution leaves a code block, the “release” method is called on the object automatically. You can control the block itself as well.
The following steps
- Create an NSAutoreleasePool object.
- Call “autorelease” to allocated objects.
- Discard the NSAutoreleasePool object
1 2 3 |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; id obj = [[NSObject alloc] init]; [obj autorelease]; [pool drain]; |
In the Cocoa Framework, NSAutoreleasePool objects are created, owned, or disposed of all over the place, such as NSRunLoop, which is the main loop of the application
But when there are too many autoreleased objects, application memory becomes short (Figure 1–14). It happens because the objects still exist until the NSAutoreleasePool object is discarded. A typical example of this is loading and resizing many images. Many autoreleased objects, such as NSData objects for reading files, UImage objects for the data, and resized images exist at the same time.
當有太多autorelease對象時, 程序的可用內存會急劇減少. 因爲這些對象會一直存在, 直到pool被結束時.
典型應用場景: 加載或調整大量圖片的時候.
|