iOSメモリのトピック:performSelectorによる即時deallocの発生
4235 ワード
今回の例は1.iPhoneベースのプロジェクト2.シミュレータでテストした.3.ARCに基づく.例は比較的簡単で、A ViewControllerはB ViewControllerを起動する.
主なコードはB ViewControllerにあります.
コードは次のことを示します.
[self performSelector:@selector(block1) withObject:nil afterDelay:3.0f];
block 1が呼び出されました.
起動して、closeボタンをクリックして、B ViewControllerを閉じて、印刷結果を表示します.
3 s後に次のように印刷します.
実は、私が望んでいる結果は、closeの後ですぐにdeallocを呼び出しますが、今は3 s後に呼び出す必要があります.
何度も切り替えて一定回数に達したら、メモリが漏れるのではないかと考えてみましょう.
我慢できない!
block 1でselfを使ったため、すぐに解放できないのではないかと思うかもしれません.
では、block 2を呼び出します.
[self performSelector:@selector(block2:) withObject:nil afterDelay:3.0f];
結果を見て、やはり3 s後に実行します
block 2がblock 1と異なるのは、weakを用いて現在のself(BViewcontroller)を参照することである.
今回は、少なくともselfはnullだと教えてくれました.
block 3を呼び出して、新しい発見があるかどうか見てみましょう.
[self performSelector:@selector(block3) withObject:nil afterDelay:3.0f];
やはり3 s後に結果が得られます
よし、酔っ払った!
いったい元凶は誰なのか.
答えは次のとおりです.
performSelector......
呼び出し
[self performSelector:@selector(block3) withObject:nil afterDelay:3.0f];
私こそ現在のオブジェクトBViewcontrollerにstongの引用があり、MRCではretainと理解できます.
これにより、close時に直ちに自分を解放することができない(deallocメソッドは直ちに呼び出されない).
実は、appleはperformSelectorをキャンセルする方法を提供してくれました.
closeメソッドのコードの変更
再度実行すると、deallocメソッドが直ちに呼び出されたことがわかります.
主なコードはB ViewControllerにあります.
@interface BViewController ()
@property (strong, nonatomic) NSMutableArray *tmpData;
@end
@implementation BViewController
- (void)dealloc
{
NSLog(@"---------------------------");
NSLog(@"MyViewController dealloc.");
NSLog(@"---------------------------");
}
- (void)viewDidLoad
{
[super viewDidLoad];
_tmpData = [NSMutableArray arrayWithObjects:@"mark.z", nil];
UIButton *cloneMeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[cloneMeBtn setTitle:@"close" forState:UIControlStateNormal];
cloneMeBtn.backgroundColor = [UIColor blueColor];
cloneMeBtn.frame = CGRectMake(130, 300, 80, 50);
[self.view addSubview:cloneMeBtn];
[cloneMeBtn addTarget:self action:@selector(close) forControlEvents:UIControlEventTouchUpInside];
[self performSelector:@selector(block1) withObject:nil afterDelay:3.0f];
}
- (void)block1
{
[UIView animateWithDuration:1 animations:^{
NSLog(@"tmpArray = %@", self.tmpData);
[self.tmpData addObject:@"hk"];
NSMutableArray *array = self.tmpData;
[array addObject:@"ju"];
} completion:^(BOOL finished) {
}];
}
- (void)block2:(id)sender
{
NSLog(@"sender = %@", sender);
MyViewController __weak *weakSelf = sender;
[UIView animateWithDuration:1 animations:^{
NSLog(@"tmpArray = %@", weakSelf.tmpData);
[weakSelf.tmpData addObject:@"hk"];
NSMutableArray *array = weakSelf.tmpData;
[array addObject:@"ju"];
} completion:^(BOOL finished) {
}];
}
- (void)block3
{
[UIView animateWithDuration:1 animations:^{
NSLog(@"tmpArray = %@", _tmpData);
[_tmpData addObject:@"hk"];
NSMutableArray *array = _tmpData;
[array addObject:@"ju"];
NSLog(@"tmpArray = %@", _tmpData);
} completion:^(BOOL finished) {
}];
}
- (void)close
{
[self dismissViewControllerAnimated:YES completion:^{
}];
}
@end
コードは次のことを示します.
[self performSelector:@selector(block1) withObject:nil afterDelay:3.0f];
block 1が呼び出されました.
起動して、closeボタンをクリックして、B ViewControllerを閉じて、印刷結果を表示します.
3 s後に次のように印刷します.
tmpArray = (
"mark.z"
)
---------------------------
MyViewController dealloc.
---------------------------
実は、私が望んでいる結果は、closeの後ですぐにdeallocを呼び出しますが、今は3 s後に呼び出す必要があります.
何度も切り替えて一定回数に達したら、メモリが漏れるのではないかと考えてみましょう.
我慢できない!
block 1でselfを使ったため、すぐに解放できないのではないかと思うかもしれません.
では、block 2を呼び出します.
[self performSelector:@selector(block2:) withObject:nil afterDelay:3.0f];
結果を見て、やはり3 s後に実行します
sender = (null)
tmpArray = (null)
---------------------------
MyViewController dealloc.
---------------------------
いいえselfの問題です.block 2がblock 1と異なるのは、weakを用いて現在のself(BViewcontroller)を参照することである.
今回は、少なくともselfはnullだと教えてくれました.
block 3を呼び出して、新しい発見があるかどうか見てみましょう.
[self performSelector:@selector(block3) withObject:nil afterDelay:3.0f];
やはり3 s後に結果が得られます
tmpArray = (
"mark.z"
)
tmpArray = (
"mark.z",
hk,
ju
)
---------------------------
MyViewController dealloc.
---------------------------
よし、酔っ払った!
いったい元凶は誰なのか.
答えは次のとおりです.
performSelector......
呼び出し
[self performSelector:@selector(block3) withObject:nil afterDelay:3.0f];
私こそ現在のオブジェクトBViewcontrollerにstongの引用があり、MRCではretainと理解できます.
これにより、close時に直ちに自分を解放することができない(deallocメソッドは直ちに呼び出されない).
実は、appleはperformSelectorをキャンセルする方法を提供してくれました.
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
closeメソッドのコードの変更
- (void)close
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self dismissViewControllerAnimated:YES completion:^{
}];
}
再度実行すると、deallocメソッドが直ちに呼び出されたことがわかります.