Tip
- 1.UIImageView延遲加載照片
- 2.線程保活
- 3.子線程中執行NSTimer
- 4.performSelector
- 5.自動釋放池
一.UIImageView延遲加載照片
在實際的開發過程中和面試題中,我們總去說可以在tableview的滑動的時候不去加載照片,因為渲染可能會阻塞主線程,我們總說:可以再tableview停止滑動的時候再去渲染照片,現在可以好好聊聊這個話題
//控制中寫上這個方法即可,3秒鐘然后顯示照片
//這個方法,我看就可以在滑動的時候,延遲顯示照片
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.imageview performSelector:@selector(setImage:)
withObject:[UIImage imageNamed:@"123.png"]
afterDelay:3];
}
//這個是第二種清空,然后直接指定什么模式,二者等價
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.imageview performSelector:@selector(setImage:)
withObject:[UIImage imageNamed:@"123.png"]
afterDelay:3
inModes:@[NSDefaultRunLoopMode]];
}
滑動的時候,阻止了照片顯示,因為它默認是在Default下顯示,
此時tableView在滑動,RunLoop屬于Tracking的模式,
所以3秒鐘顯示的目的被延時了
- 如果想讓照片的顯示或者timer的運行在任何時候都好使怎么辦?
設置為commonMode就行了
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.imageview performSelector:@selector(setImage:)
withObject:[UIImage imageNamed:@"123.png"]
afterDelay:3
inModes:@[NSRunLoopCommonModes]];
}
二.線程保活
可能你的項目中需要一個線程,一直在后臺做些耗時操作,但是不影響主線程,我們不要一直大量的創建和銷毀線程,因為這樣太浪費性能了,我們只要保留這個線程,只要對他進行“保活”就行
//繼承了一個NSTread 線程,然后使用vc中創建和執行某個任務,查看線程的情況
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
WXThread *thread = [[WXThread alloc] initWithTarget:self
selector:@selector(doSomeThing)
object:nil];
[thread start];
}
- (void)doSomeThing{
NSLog(@"doSomeThing");
}
//每一次點擊屏幕的時候,線程執行完方法,直接釋放掉了,下一次創建了一個新的線程;
//子線程存活的時間很短,只要執行完畢任務,就會被釋放
2017-04-19 16:03:10.686 WXAllTest[14928:325108] doSomeThing
2017-04-19 16:03:10.688 WXAllTest[14928:325108] WXTread - dealloc - <WXThread: 0x600000276780>{number = 3, name = (null)}
2017-04-19 16:03:18.247 WXAllTest[14928:325194] doSomeThing
2017-04-19 16:03:18.249 WXAllTest[14928:325194] WXTread - dealloc - <WXThread: 0x608000271340>{number = 4, name = (null)}
2017-04-19 16:03:23.780 WXAllTest[14928:325236] doSomeThing
2017-04-19 16:03:23.781 WXAllTest[14928:325236] WXTread - dealloc - <WXThread: 0x608000270e00>{number = 5, name = (null)}
如果我每隔一段時間就像在線程中執行某個操作,好像現在不行
如果我們將線程對象強引用,也是不行的,會崩潰
1.成為基本屬性
/** 線程對象 */
@property(strong,nonatomic) WXThread *thread;
2.創建線程之后,直接將入到RunLoop中
- (void)viewDidLoad {
[super viewDidLoad];
_thread = [[WXThread alloc] initWithTarget:self
selector:@selector(doSomeThing)
object:nil];
[_thread start];
}
3.執行doSomeThing函數
- (void)doSomeThing{
//一定要加入一個timer,port,或者是obervers,否則RunLoop啟動不起來
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
4.在點擊屏幕的時候,執行一個方法,線程之間的數據通信
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self performSelector:@selector(test) onThread:_thread withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
}
5.將test方法寫清楚
- (void)test{
NSLog(@"current thread - %@",[NSThread currentThread]);
}
//打印結果:同一個線程,線程保活成功
2017-04-19 18:21:07.660 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:07.843 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:08.015 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:08.194 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:08.398 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:08.598 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:08.770 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
三.子線程中執行NSTimer
剛才學習了在RunLoop中去處理源,source (seletor),現在來看看如何在子線程中處理NSTimer
/** 線程對象 */
@property(strong,nonatomic) WXThread *thread;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//延遲圖片加載
_thread = [[WXThread alloc] initWithTarget:self
selector:@selector(execute)
object:nil];
[_thread start];
}
- (void)execute{
//該方法默認不加入RunLoop中,使用schedule可以
NSTimer *timer = [NSTimer timerWithTimeInterval:0.3
target:self
selector:@selector(test2)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
- (void)test2{
NSLog(@"current thread ********** 2 - %@",[NSThread currentThread]);
}
//打印:
2017-04-19 18:45:18.342 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:18.643 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:18.943 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:19.243 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:19.544 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:19.842 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:20.143 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:20.443 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:20.743 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:21.042 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:21.342 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
注意: 這兩者相同,后者默認直接創建了RunLoop,然后加進去了,但是一定要run才能啟動,但是過去我們沒有在主線程中run,也好使啊,為毛?因為在主線程中系統自動run了,否則線程早就停止了
NSTimer *timer = [NSTimer timerWithTimeInterval:0.3
target:self
selector:@selector(test2)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(test2)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] run];
疑惑:滑動textview的時候,模式已經改成了Tracking,但是我們的timer是default模式,為什么沒有影響?
因為textView是主線程的,而timer是子線程的東西,所以沒關系
五.自動釋放池
1.什么是自動釋放池,就是將目前使用到的對象放到池子中,然后當自動釋放池銷毀的時候,對內部的所有對象都進行realese操作,retrainCount減一
2.自動釋放池什么時候銷毀和創建?
當每一次要進入睡眠狀態,那么就會銷毀,當即將醒來的時候,重新創建
一個RunLoop對應一個線程
建議每一次啟動RunLoop的時候,包裝一個自動釋放池,臨時創建了很多對象,等著我們釋放,在很多優秀的開源庫中,都有這個說明
- (void)viewDidLoad {
[super viewDidLoad];
//延遲圖片加載
_thread = [[WXThread alloc] initWithTarget:self
selector:@selector(execute)
object:nil];
[_thread start];
}
- (void)execute{
// 該方法默認不加入RunLoop中,使用schedule可以
@autoreleasepool {
NSTimer *timer = [NSTimer timerWithTimeInterval:0.3
target:self
selector:@selector(test2)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
}