@implementation ViewController
- (void)viewDidLoad {[super viewDidLoad];__unsafe_unretained NSObject *obj1 = [ViewController getObj];NSLog(@"=====%@",obj1); // 運行OK__unsafe_unretained NSObject *obj2 = [ViewController getObj];NSLog(@"=====%@",obj2); // crash 野指針
}+ (id)getObj {return [NSObject new];
}
復制代碼
- (void)viewDidLoad {[super viewDidLoad];__weak NSObject *obj1 = [ViewController getObj];NSLog(@"=====%@",obj1); // nil__weak NSObject *obj2 = [ViewController getObj];NSLog(@"=====%@",obj2);// 輸出對象
}
復制代碼
Autorelease與Autoreleasepool
參考: ARC環境下編譯器到底對autorelease對象做了怎樣的優化 黑幕背后的Autorelease 自動釋放池的前世今生 ---- 深入解析 Autoreleasepool Objective-C 小記(8)autorelease
####autoreleasepool 1、自動釋放池是由 AutoreleasePoolPage 以雙向鏈表的方式實現的 2、當對象調用 autorelease 方法時,會將對象加入 AutoreleasePoolPage 的棧中 3、調用 AutoreleasePoolPage::pop 方法會向棧中的對象發送 release 消息 @autoreleasepool{}
@autoreleasepool {__autoreleasing NSObject *obj = [NSObject new];}
復制代碼
偽代碼
// 獲取哨兵POOL_SENTINELvoid * atautoreleasepoolobj = objc_autoreleasePoolPush();{__autoreleasing NSObject *obj = [NSObject new];}// 就是release哨兵之后的autorelease對象。objc_autoreleasePoolPop(atautoreleasepoolobj);
復制代碼
autorelease調用棧
- [NSObject autorelease]
└── id objc_object::rootAutorelease()└─ id objc_object::rootAutorelease2()└─ static id AutoreleasePoolPage::autorelease(id obj)└─ static id AutoreleasePoolPage::autoreleaseFast(id obj)├─ id *add(id obj)├─ static id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)│ ├─ AutoreleasePoolPage(AutoreleasePoolPage *newParent)│ └─ id *add(id obj)└─ static id *autoreleaseNoPage(id obj)├─ AutoreleasePoolPage(AutoreleasePoolPage *newParent)└─ id *add(id obj)
復制代碼
#####一個autorelease對象在什么時刻釋放? 1、手動指定Autoreleasepool:當前Autoreleasepool作用域大括號結束時釋放;
2、不手動指定:autorelease對象會被添加到最近一次創建的autoreleasepool中,并在當前的runloop迭代結束時候釋放。
例如:主Runloop對Autoreleasepool管理的流程: Runloop中,檢測到觸摸事件,創建事件,創建Autoreleasepool,autorelease對象加入pool中,事件完成,Runloop運行循環將要結束,釋放Autoreleasepool,向pool中對象發送release消息,Runloop休眠。 ####autorelease 進行的非持有方法的優化 1、alloc/new/copy/mutableCopy---持有對象方法 2、其他類方法返回的對象,如果下面的createObj
@implementation BBObject
+ (instancetype)createObj {return [self new];
}
復制代碼
需要了解下面的方法:
id objc_autoreleaseReturnValue(id obj)
{// prepareOptimizedReturn判斷是否可以TSL優化,可以則標記,YES--就不需要調用 objc_autorelease(),優化性能if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;return objc_autorelease(obj);
}
id objc_retainAutoreleasedReturnValue(id obj)
{// 如果之前 objc_autoreleaseReturnValue() 存入的標志位為 ReturnAtPlus1,則直接返回對象,無需調用 objc_retain(),優化性能if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;return objc_retain(obj);
}
復制代碼
static ALWAYS_INLINE bool
prepareOptimizedReturn(ReturnDisposition disposition)
{assert(getReturnDisposition() == ReturnAtPlus0);if (callerAcceptsOptimizedReturn(__builtin_return_address(0))) {if (disposition) setReturnDisposition(disposition);return true;}return false;
}static ALWAYS_INLINE ReturnDisposition
acceptOptimizedReturn()
{ReturnDisposition disposition = getReturnDisposition();setReturnDisposition(ReturnAtPlus0); // reset to the unoptimized statereturn disposition;
}
復制代碼
TLS 全稱為 Thread Local Storage,是每個線程專有的鍵值存儲
在某個線程上的函數調用棧上相鄰兩個函數對 TLS 進行了存取,這中間肯定不會有別的程序『插手』。
所以 getReturnDisposition() 和 setReturnDisposition() 的實現比較簡單,不需要判斷考慮是針對哪個對象的 Disposition 進行存取,因為當前線程上下文中只處理唯一的對象,保證不會亂掉。 static ALWAYS_INLINE void
setReturnDisposition(ReturnDisposition disposition)
{tls_set_direct(RETURN_DISPOSITION_KEY, (void*)(uintptr_t)disposition);
}
復制代碼
callerAcceptsOptimizedReturn(__builtin_return_address(0))函數在不同架構的 CPU 上實現也是不一樣的。 主要作用:
__builtin_return_address(0)獲取當前函數返回地址,傳入 callerAcceptsOptimizedReturn 判斷調用方是否緊接著調用了 objc_retainAutoreleasedReturnValue 當判斷調用方緊接著調用了 objc_retainAutoreleasedReturnValue 或者 objc_unsafeClaimAutoreleasedReturnValue 直接當前對象地址,而不執行retain與autorelease操作.
#####ARC 會視情況在調用方法時可能會添加 retain ,在方法內部返回時可能會添加 autorelease ,經過優化后很可能會抵消。
- (void)test {[BBObject new];
}
復制代碼
編譯器編譯后的偽代碼
- (void)test {objc_release([BBObject new]) ;
}
復制代碼
#####2、持有、局部變量引用 __strong
- (void)test {__strong BBObject * obj = [BBObject new];
}
復制代碼
編譯器編譯后的偽代碼
- (void)test {id temp = [BBObject new];objc_storeStrong(&tmp,nil);//相當于tmp指向對象執行release
}
復制代碼
__weak、__unsafe_unretained
// 這種寫法xcode提示警告__weak BBObject * obj = [BBObject new]; __unsafe_unretained BBObject * obj = [BBObject new];
復制代碼
#####3、持有、外部變量引用
- (void)test {self.obj = [BBObject new];
}
復制代碼
編譯器編譯后的偽代碼
- (void)test{id temp = [BBObject new];[self setObj:temp];//setter方法執行objc_storeStrongobjc_release(temp);
}
- (void)setObj:(id aObj) {objc_storeStrong(&_obj, aObj);
}
復制代碼
#####4、不持有、無引用
- (void)test {[BBObject createObj];
}
復制代碼
編譯器編譯后的偽代碼
+ (instancetype) createObj {id tmp = [self new];return objc_autoreleaseReturnValue(tmp); // 系統可能會調用[tmp autorelease]
}
- (void)test { objc_unsafeClaimAutoreleasedReturnValue([BBObject createObj]);
}
復制代碼
#####5、不持有、局部變量引用
- (void)test {BBObject * obj1 = [BBObject createObj];
}
復制代碼
編譯器編譯后的偽代碼
+ (instancetype) createObj {id tmp = [self new];return objc_autoreleaseReturnValue(tmp); // 系統可能會調用[tmp autorelease]
}
- (void)test {id obj1 = objc_retainAutoreleasedReturnValue([BBObject createObj]); objc_storeStrong(& obj1,nil);
}
復制代碼
發現obj1指向的對象不會加入autoreleasepool
#####6、不持有、外部變量引用
+ (instancetype) createObj {id tmp = [self new];return objc_autoreleaseReturnValue(tmp); // 系統可能會調用[tmp autorelease]
}
- (void)test {self.obj = [BBObject createObj];
}
復制代碼
編譯后的偽代碼
- (void)test {id tmp = _objc_retainAutoreleasedReturnValue([Foo createFoo]); [self setObj:temp]; // setter方法執行objc_storeStrongobjc_release(temp);
}
復制代碼
查看autoreleasepool中的對象方法: 1、extern void _objc_autoreleasePoolPrint(void); //extern這個方法,需要查看的地方使用_objc_autoreleasePoolPrint();
2、需要查看的地方打斷點,然后po _objc_autoreleasePoolPrint()