weak 安全性很好!一旦沒有強引用,自動將地址設置為 nil,OC中可以向 nil 發送任何消息都不會抱錯!
autorelease 作用就是延遲釋放
,給對象添加延遲釋放的標記
聲明變量
@property (nonatomic, assign) UIButton *btn;
向控制器View中添加按鈕:
// alloc / init - 應該出了作用域才會被釋放
//
self.btn = [[UIButton alloc] init];
self.btn.frame = CGRectMake(40, 40, 100, 100);
self.btn.backgroundColor = [UIColor orangeColor];
[self.view addSubview:self.btn];
NSLog(@"%@", self.view.subviews);
// 類方法創建的 - 是 ‘autorelease’ 的
// 出了作用域,會被添加到自動釋放池中
// 自動釋放池被銷毀前,才釋放對象!
self.btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
[self.view addSubview:self.btn];
NSLog(@"%@", self.view.subviews);
// ARC 上述演練不好使!以 weak 為主!
運行結果:
2015-03-25 10:31:20.055 09-自動釋放池[27279:718327] (
"<_UILayoutGuide: 0x7fc5d0d346b0; frame = (0 0; 0 0); hidden = YES; layer = >",
"<_UILayoutGuide: 0x7fc5d0e77950; frame = (0 0; 0 0); hidden = YES; layer = >",
">"
)
2015-03-25 10:31:20.063 09-自動釋放池[27279:718327] (
"<_UILayoutGuide: 0x7fc5d0d346b0; frame = (0 0; 0 0); hidden = YES; layer = >",
"<_UILayoutGuide: 0x7fc5d0e77950; frame = (0 0; 0 0); hidden = YES; layer = >",
">",
">"
)
可以看到,無論是alloc還是buttonWithType: 都是可以添加到控制器的View上的,也就意味著,創建出來的button並沒有立刻銷毀
野指針
錯誤autorealease
的,出了作用域,會自定添加到自動釋放池
中,自動釋放池銷毀前,才會釋放對象,同樣assign聲明的變量會持有該對象的地址,一直不肯放~
Person.h
@interface Person : NSObject
+ (instancetype)person;
@end
Person.m
@implementation Person
+ (instancetype)person {
// autorelease 作用就是延遲釋放
// 給對象添加延遲釋放的標記
Person *p = [[Person alloc] init] ;
return p;
}
- (void)dealloc {
NSLog(@"我去了");
[super dealloc];
}
@end
聲明Persion變量:
@property (nonatomic, assign) Person *p;
測試Demo:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.p = [Person person];
NSLog(@"%@", self.p);
}
運行結果:
2015-03-25 10:44:16.652 10-ARC自動釋放[27546:726446]
在MRC下,如果提供的類方法沒有autorelease,那麼在調用的時候,用assign變量接收,仍然會打印出對象的地址,當然對象並沒有銷毀(dealloc沒有輸出
)。
Person *p = [[[Person alloc] init] autorelease];
打印結果:
2015-03-25 10:56:04.224 10-ARC自動釋放[27690:732059]
2015-03-25 10:56:04.238 10-ARC自動釋放[27690:732059] 我去了
可以看到對象被釋放了,說明autorelease給對象添加了延遲釋放
的標記,讓對象在自動是釋放池銷毀,或者出了離它最近的自動釋放池作用域的時候,自動釋放。
變量的聲明:
@property (nonatomic, weak) Person *p;
@property (nonatomic, weak) UIButton *button;
測試Demo:
// ARC 中,如果給 weak 做 alloc / init 的設置數值,Xcode 會提示!
self.p = [[Person alloc] init];
// 對於 Xcode 編譯器而言,只要是類方法,就不會提示!
// 如果使用 MRC 開發,所有 返回 instancetype 的類方法,返回的對象都是 autorelease 的!
// 因為沒有強引用,會被立即釋放
self.p = [Person person];
// ARC中之所以為 null,是因為 weak,跟autorelease沒有關系!
NSLog(@"%@", self.p);
// 之所以會這樣是因為蘋果框架的底層是用 MRC 寫的,buttonWithType返回的對象是帶有 autorelease
self.button = [UIButton buttonWithType:UIButtonTypeContactAdd];
NSLog(@"%@", self.button);
打印結果:
2015-03-25 11:00:58.756 10-ARC自動釋放[27778:735123] (null)
2015-03-25 11:00:58.769 10-ARC自動釋放[27778:735123] >
autorelease
的,意味著會延遲銷毀,僅僅是被添加到自動釋放池而已,等待著出自動釋放池作用域或者自動釋放池銷毀。而不會立刻銷毀。
問題:以下代碼是否有問題?如果有,如何解決?
for (long i = 0; i < largeNumber; ++i) {
@autoreleasepool {
NSString *str = [NSString stringWithFormat:@"hello - %ld", i];
str = [str uppercaseString];
str = [str stringByAppendingString:@" - world"];
}
}
答案:如果 largeNumber 非常大,會創建太多自動釋放的對象,有可能會把自動釋放池"撐滿" 提示:經常一次用戶交互,遠遠不止一個 for,在 for 的前後,會有很多的代碼,但是這個 for 會占用大量的自動釋放池空間
引入自動釋放池,網絡上有兩種解決方案!
1> 外面加自動釋放池(快):能夠保證for循環結束後,內部產生的自動釋放對象,都會被銷毀
需要等到 for 結束後,才會釋放內存
2> 裡面加自動釋放池(慢):能夠每一次 for 都釋放產生的自動釋放對象!
提問:那種方式效率高!速度差不多!大多數測試內部比外部外!
如果面試中問道:要說裡面的略快,“親測”!!!
提示:在日常工作中!有的時候,真的會出現代碼內存飙升的情況!要知道解決辦法!
設置largeNumner的數值:
long largeNumber = 5 * 1000 * 1000;
測試Demo:
NSLog(@"內部 start");
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
for (long i = 0; i < largeNumber; ++i) {
@autoreleasepool {
NSString *str = [NSString stringWithFormat:@"hello - %ld", i];
str = [str uppercaseString];
str = [str stringByAppendingString:@" - world"];
}
}
NSLog(@"end - %f", CFAbsoluteTimeGetCurrent() - start);
[self answer1];
answer1方法:
NSLog(@"外面 start");
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
@autoreleasepool {
for (long i = 0; i < largeNumber; ++i) {
NSString *str = [NSString stringWithFormat:@"hello - %ld", i];
str = [str uppercaseString];
str = [str stringByAppendingString:@" - world"];
}
}
NSLog(@"end - %f", CFAbsoluteTimeGetCurrent() - start);
測試結果:
2015-03-25 11:18:50.663 12-自動釋放池的面試題[28015:743360] 內部 start
2015-03-25 11:19:03.996 12-自動釋放池的面試題[28015:743360] end - 13.332371
2015-03-25 11:19:03.996 12-自動釋放池的面試題[28015:743360] 外面 start
2015-03-25 11:19:18.227 12-自動釋放池的面試題[28015:743360] end - 14.230752
由測試結果可以知道,對於500w次循環,內部、外部添加autorelease,速度其實差不多,內部略微快一點,但這是取決於編譯器。 但是,在外部添加autorelease會占用極大的內存(注意,這裡跟不加autorelease不一樣,不加autorelease開辟的內存是不會釋放的)。 開發中,通常在內部添加。