แอนิเมชั่น iOS และการปรับแต่งเพื่อประสิทธิภาพ
เผยแพร่แล้ว: 2022-03-11การสร้างแอปที่ยอดเยี่ยมไม่ได้เกี่ยวกับรูปลักษณ์หรือฟังก์ชันการทำงานเท่านั้น แต่ยังเกี่ยวกับประสิทธิภาพของแอปด้วย แม้ว่าข้อกำหนดฮาร์ดแวร์ของอุปกรณ์มือถือจะดีขึ้นอย่างรวดเร็ว แต่แอปที่ทำงานได้ไม่ดี กระตุกทุกครั้งที่เปลี่ยนหน้าจอหรือเลื่อนเหมือนสไลด์โชว์สามารถทำลายประสบการณ์ของผู้ใช้และกลายเป็นสาเหตุของความหงุดหงิด ในบทความนี้ เราจะมาดูวิธีการวัดประสิทธิภาพของแอพ iOS และปรับแต่งให้มีประสิทธิภาพ สำหรับวัตถุประสงค์ของบทความนี้ เราจะสร้างแอปที่เรียบง่ายพร้อมรายการรูปภาพและข้อความจำนวนมาก
เพื่อวัตถุประสงค์ในการทดสอบประสิทธิภาพ ผมขอแนะนำให้ใช้อุปกรณ์จริง หากคุณจริงจังกับการสร้างแอปและปรับแต่งแอปเพื่อแอนิเมชั่น iOS ที่ลื่นไหล ตัวจำลองก็ไม่ควรมองข้าม การจำลองบางครั้งอาจไม่เป็นไปตามความเป็นจริง ตัวอย่างเช่น เครื่องจำลองอาจทำงานบน Mac ของคุณ ซึ่งอาจหมายความว่า CPU (หน่วยประมวลผลกลาง) มีประสิทธิภาพมากกว่า CPU บน iPhone ของคุณมาก ในทางกลับกัน GPU (หน่วยประมวลผลกราฟิก) แตกต่างกันมากระหว่างอุปกรณ์และ Mac ของคุณ ซึ่ง Mac ของคุณจะเลียนแบบ GPU ของอุปกรณ์จริงๆ ด้วยเหตุนี้ การทำงานของ CPU-bound มักจะเร็วขึ้นในเครื่องจำลองของคุณ ในขณะที่การทำงานที่ผูกกับ GPU มักจะช้าลง
เคลื่อนไหวที่ 60 FPS
ลักษณะสำคัญประการหนึ่งของประสิทธิภาพการรับรู้คือการทำให้แน่ใจว่าแอนิเมชั่นของคุณทำงานที่ 60 FPS (เฟรมต่อวินาที) ซึ่งเป็นอัตราการรีเฟรชของหน้าจอของคุณ มีแอนิเมชั่นตามเวลาซึ่งเราจะไม่พูดถึงที่นี่ โดยทั่วไปแล้ว หากคุณใช้อะไรที่มากกว่า 50 FPS แอปของคุณจะดูราบรื่นและมีประสิทธิภาพ หากภาพเคลื่อนไหวของคุณติดอยู่ระหว่าง 20 ถึง 40 FPS จะมีอาการกระตุกอย่างเห็นได้ชัด และผู้ใช้จะตรวจพบ "ความหยาบ" ในการเปลี่ยนภาพ สิ่งที่ต่ำกว่า 20 FPS จะส่งผลอย่างมากต่อการใช้งานแอปของคุณ
ก่อนที่เราจะเริ่ม คุณควรพูดถึงความแตกต่างระหว่างการดำเนินการที่ผูกไว้กับ CPU และ GPU ก่อน GPU เป็นชิปพิเศษที่ได้รับการปรับให้เหมาะกับการวาดกราฟิก แม้ว่า CPU จะทำได้เช่นกัน แต่ก็ช้ากว่ามาก นี่คือเหตุผลที่เราต้องการลดการแสดงผลกราฟิกของเรา กระบวนการสร้างภาพจากโมเดล 2D หรือ 3D ไปยัง GPU แต่เราต้องระวัง เนื่องจากเมื่อ GPU หมดกำลังในการประมวลผล ประสิทธิภาพที่เกี่ยวข้องกับกราฟิกจะลดลงแม้ว่า CPU จะค่อนข้างว่างก็ตาม
Core Animation เป็นเฟรมเวิร์กที่ทรงพลังที่จัดการแอนิเมชั่นทั้งภายในแอพของคุณและภายนอก แบ่งกระบวนการออกเป็น 6 ขั้นตอนสำคัญ:
เลย์เอาต์: ตำแหน่งที่คุณจัดเรียงเลเยอร์และตั้งค่าคุณสมบัติของเลเยอร์ เช่น สีและตำแหน่งสัมพัทธ์
จอแสดงผล: นี่คือที่ที่รูปภาพสำรองถูกวาดลงบนบริบท รูทีนใดๆ ที่คุณเขียนใน
drawRect:
หรือdrawLayer:inContext:
เข้าถึงได้ที่นี่เตรียม: ในขั้นตอนนี้ Core Animation กำลังจะส่งบริบทไปยังตัวแสดงภาพเพื่อวาด ทำงานที่จำเป็นบางอย่าง เช่น คลายการบีบอัดรูปภาพ
ยอมรับ: ที่นี่ Core Animation จะส่งข้อมูลทั้งหมดนี้ไปยังเซิร์ฟเวอร์แสดงผล
การดีซีเรียลไลซ์เซชั่น: 4 ขั้นตอนก่อนหน้านี้อยู่ในแอปของคุณ ตอนนี้กำลังประมวลผลแอนิเมชันนอกแอปของคุณ เลเยอร์ที่จัดแพ็กเกจจะถูกดีซีเรียลไลซ์เป็นทรีที่เซิร์ฟเวอร์การเรนเดอร์สามารถเข้าใจได้ ทุกอย่างถูกแปลงเป็นเรขาคณิต OpenGL
วาด: แสดงรูปร่าง (จริงๆ แล้วเป็นรูปสามเหลี่ยม)
คุณอาจเดาได้ว่ากระบวนการ 1-4 เป็นการทำงานของ CPU และ 5-6 คือการทำงานของ GPU ในความเป็นจริง คุณสามารถควบคุมได้เพียง 2 ขั้นตอนแรกเท่านั้น นักฆ่าที่ใหญ่ที่สุดของ GPU คือเลเยอร์กึ่งโปร่งใสซึ่ง GPU ต้องเติมพิกเซลเดียวกันหลายครั้งต่อเฟรม นอกจากนี้ การวาดนอกหน้าจอใดๆ (เอฟเฟกต์หลายเลเยอร์ เช่น เงา หน้ากาก มุมโค้งมน หรือการแรสเตอร์ของเลเยอร์จะบังคับให้ Core Animation วาดนอกหน้าจอ) จะส่งผลต่อประสิทธิภาพเช่นกัน ภาพที่ใหญ่เกินกว่าจะประมวลผลโดย GPU จะถูกประมวลผลโดย CPU ที่ช้ากว่ามากแทน แม้ว่าเงาจะทำได้ง่ายโดยการตั้งค่าคุณสมบัติสองอย่างโดยตรงบนเลเยอร์ แต่ก็สามารถฆ่าประสิทธิภาพได้อย่างง่ายดายหากคุณมีวัตถุจำนวนมากบนหน้าจอที่มีเงา บางครั้งควรพิจารณาเพิ่มเงาเหล่านี้เป็นภาพ
การวัดประสิทธิภาพของแอนิเมชั่น iOS
เราจะเริ่มต้นด้วยแอปง่ายๆ ที่มีรูปภาพ PNG 5 รูป และมุมมองตาราง ในแอพนี้ เราจะโหลดรูปภาพทั้งหมด 5 รูป แต่จะทำซ้ำมากกว่า 10,000 แถว เราจะเพิ่มเงาให้กับทั้งรูปภาพและป้ายกำกับถัดจากรูปภาพ:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell getCustomCellIdentifier] forIndexPath:indexPath]; NSInteger index = (indexPath.row % [self.images count]); NSString *imageName = [self.images objectAtIndex:index]; NSString *filePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"png"]; UIImage *image = [UIImage imageWithContentsOfFile:filePath]; cell.customCellImageView.image = image; cell.customCellImageView.layer.shadowOffset = CGSizeMake(0, 5); cell.customCellImageView.layer.shadowOpacity = 0.8f; cell.customCellMainLabel.text = [NSString stringWithFormat:@"Row %li", (long)(indexPath.row + 1)]; cell.customCellMainLabel.layer.shadowOffset = CGSizeMake(0, 3); cell.customCellMainLabel.layer.shadowOpacity = 0.5f; return cell; }
รูปภาพถูกนำกลับมาใช้ใหม่อย่างเรียบง่ายในขณะที่ฉลากมักจะแตกต่างกัน ผลลัพธ์คือ:
เมื่อเลื่อนในแนวตั้ง คุณมักจะสังเกตเห็นอาการกระตุกเมื่อเลื่อนมุมมอง ณ จุดนี้คุณอาจคิดว่าการที่เรากำลังโหลดรูปภาพในเธรดหลักเป็นปัญหา อาจเป็นได้ถ้าเราย้ายสิ่งนี้ไปที่เธรดพื้นหลัง ปัญหาทั้งหมดของเราจะได้รับการแก้ไข
แทนที่จะคาดเดาโดยเปล่าประโยชน์ เรามาลองใช้และวัดผลการปฏิบัติงานกัน ถึงเวลาของตราสาร
หากต้องการใช้เครื่องมือ คุณต้องเปลี่ยนจาก "เรียกใช้" เป็น "โปรไฟล์" และคุณควรเชื่อมต่อกับอุปกรณ์จริงของคุณด้วย เครื่องมือบางตัวอาจไม่พร้อมใช้งานบนเครื่องจำลอง (อีกเหตุผลหนึ่งที่คุณไม่ควรเพิ่มประสิทธิภาพการทำงานบนเครื่องจำลอง!) เราจะใช้เทมเพลต "GPU Driver", "Core Animation" และ "Time Profiler" เป็นหลัก ข้อเท็จจริงที่ทราบเพียงเล็กน้อยก็คือ แทนที่จะหยุดและทำงานบนเครื่องมืออื่น คุณสามารถลากและวางเครื่องมือหลายตัวและเรียกใช้หลายตัวพร้อมกันได้
ตอนนี้เรามีการตั้งค่าเครื่องมือแล้ว มาวัดกัน ก่อนอื่น มาดูกันว่าเรามีปัญหากับ FPS จริงหรือไม่
อ๊ะ ฉันคิดว่าเราได้รับ 18 FPS ที่นี่ การโหลดรูปภาพจากบันเดิลบนเธรดหลักนั้นแพงและแพงจริงหรือ สังเกตว่าการใช้งานเรนเดอร์ของเราใกล้จะถึงขีดสุดแล้ว การใช้กระเบื้องของเราก็เช่นกัน ทั้งคู่อยู่เหนือ 95% และนั่นไม่เกี่ยวอะไรกับการโหลดรูปภาพจากบันเดิลบนเธรดหลัก ดังนั้นอย่ามองหาวิธีแก้ไขที่นี่
ปรับแต่งประสิทธิภาพ
มีคุณสมบัติที่เรียกว่า shouldRasterize และผู้คนอาจแนะนำให้คุณใช้ที่นี่ ควร Rasterize ทำอะไรกันแน่? มันแคชเลเยอร์ของคุณเป็นภาพที่แบน การวาดเลเยอร์ราคาแพงทั้งหมดนั้นต้องเกิดขึ้นครั้งเดียว ในกรณีที่เฟรมของคุณเปลี่ยนแปลงบ่อยครั้ง แคชจะไม่มีประโยชน์ เนื่องจากจะต้องสร้างใหม่ทุกครั้ง
การแก้ไขโค้ดของเราอย่างรวดเร็ว เราได้รับ:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell getCustomCellIdentifier] forIndexPath:indexPath]; NSInteger index = (indexPath.row % [self.images count]); NSString *imageName = [self.images objectAtIndex:index]; NSString *filePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"png"]; UIImage *image = [UIImage imageWithContentsOfFile:filePath]; cell.customCellImageView.image = image; cell.customCellImageView.layer.shadowOffset = CGSizeMake(0, 5); cell.customCellImageView.layer.shadowOpacity = 0.8f; cell.customCellMainLabel.text = [NSString stringWithFormat:@"Row %li", (long)(indexPath.row + 1)]; cell.customCellMainLabel.layer.shadowOffset = CGSizeMake(0, 3); cell.customCellMainLabel.layer.shadowOpacity = 0.5f; cell.layer.shouldRasterize = YES; cell.layer.rasterizationScale = [UIScreen mainScreen].scale; return cell; }
และเราวัดอีกครั้ง:
มีเพียงสองบรรทัดเท่านั้น เราได้ปรับปรุง FPS ขึ้น 2 เท่า ขณะนี้เรามีค่าเฉลี่ยที่สูงกว่า 40 FPS แต่มันจะช่วยได้ไหมถ้าเราย้ายการโหลดรูปภาพไปที่เธรดพื้นหลัง

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell getCustomCellIdentifier] forIndexPath:indexPath]; NSInteger index = (indexPath.row % [self.images count]); NSString *imageName = [self.images objectAtIndex:index]; cell.tag = indexPath.row; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSString *filePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"png"]; UIImage *image = [UIImage imageWithContentsOfFile:filePath]; dispatch_async(dispatch_get_main_queue(), ^{ if (indexPath.row == cell.tag) { cell.customCellImageView.image = image; } }); }); cell.customCellImageView.layer.shadowOffset = CGSizeMake(0, 5); cell.customCellImageView.layer.shadowOpacity = 0.8f; cell.customCellMainLabel.text = [NSString stringWithFormat:@"Row %li", (long)(indexPath.row + 1)]; cell.customCellMainLabel.layer.shadowOffset = CGSizeMake(0, 3); cell.customCellMainLabel.layer.shadowOpacity = 0.5f; // cell.layer.shouldRasterize = YES; // cell.layer.rasterizationScale = [UIScreen mainScreen].scale; return cell; }
เมื่อทำการวัด เราจะเห็นว่าประสิทธิภาพนั้นอยู่ที่ประมาณ 18 FPS:
ไม่มีอะไรควรค่าแก่การฉลอง กล่าวอีกนัยหนึ่ง มันไม่ได้ทำให้อัตราเฟรมของเราดีขึ้น นั่นเป็นเพราะแม้ว่าการอุดตันของเธรดหลักจะไม่ถูกต้อง แต่ก็ไม่ใช่คอขวดของเรา
เมื่อย้อนกลับไปที่ตัวอย่างที่ดีกว่า ซึ่งเราเฉลี่ยที่ 40FPS นั้น ประสิทธิภาพก็ราบรื่นขึ้นอย่างเห็นได้ชัด แต่เราสามารถทำได้ดีกว่าจริงๆ
การตรวจสอบ "Color Blended Layers" บนเครื่องมือ Core Animation เราจะเห็น:
“Color Blended Layers” จะแสดงบนหน้าจอที่ GPU ของคุณทำการเรนเดอร์เป็นจำนวนมาก สีเขียวหมายถึงจำนวนกิจกรรมการเรนเดอร์ที่น้อยที่สุด ในขณะที่สีแดงหมายถึงจำนวนกิจกรรมที่มากที่สุด แต่เราตั้งค่า shouldRasterize เป็น YES
เป็นที่น่าสังเกตว่า "Color Blended Layers" ไม่เหมือนกับ "Color Hits Green and Misses Red" ในภายหลังโดยพื้นฐานแล้วจะเน้นเลเยอร์แรสเตอร์เป็นสีแดงในขณะที่แคชถูกสร้างขึ้นใหม่ (เครื่องมือที่ดีเพื่อดูว่าคุณไม่ได้ใช้แคชอย่างถูกต้องหรือไม่) การตั้งค่าควรแรสเตอร์ไรซ์เป็น YES
จะไม่มีผลต่อการเรนเดอร์เริ่มต้นของเลเยอร์ที่ไม่ทึบแสง
นี่เป็นประเด็นสำคัญ และเราจำเป็นต้องหยุดคิดสักครู่ ไม่ว่า shouldRasterize จะถูกตั้งค่าเป็น YES
หรือไม่ก็ตาม ในการเรนเดอร์เฟรมเวิร์กจำเป็นต้องตรวจสอบมุมมองทั้งหมด และผสมผสาน (หรือไม่) โดยขึ้นอยู่กับว่าการดูย่อยมีความโปร่งใสหรือทึบแสง แม้ว่า UILabel ของคุณอาจไม่มีความทึบ แต่ก็อาจไร้ค่าและทำให้ประสิทธิภาพการทำงานของคุณแย่ลง ตัวอย่างเช่น UILabel แบบโปร่งใสบนพื้นหลังสีขาวอาจไร้ค่า มาทำให้ทึบกันเถอะ:
สิ่งนี้ให้ประสิทธิภาพที่ดีขึ้น แต่รูปลักษณ์และความรู้สึกของแอพของเราเปลี่ยนไป เนื่องจากป้ายกำกับและรูปภาพของเราเป็นแบบทึบ เงาจึงเคลื่อนไปรอบๆ รูปภาพของเรา คงไม่มีใครชอบการเปลี่ยนแปลงนี้ และหากเราต้องการรักษารูปลักษณ์และความรู้สึกดั้งเดิมไว้ด้วยประสิทธิภาพระดับสูงสุด เราจะไม่หมดหวัง
ในการบีบ FPS พิเศษออกมาในขณะที่รักษารูปลักษณ์ดั้งเดิมไว้ สิ่งสำคัญคือต้องทบทวนสองขั้นตอนหลักของแอนิเมชั่นหลักที่เรามองข้ามไป
- เตรียมตัว
- ให้สัญญา
สิ่งเหล่านี้อาจดูเหมือนหมดไปจากมือของเรา แต่นั่นไม่เป็นความจริงเลย เราทราบดีว่าการโหลดรูปภาพจะต้องคลายการบีบอัด เวลาในการขยายจะเปลี่ยนไปตามรูปแบบภาพ สำหรับการคลายการบีบอัด PNG จะเร็วกว่า JPEG มาก (แม้ว่าการโหลดจะนานกว่า และขึ้นอยู่กับขนาดภาพด้วย) ดังนั้นเราจึงอยู่ในเส้นทางที่ถูกต้องในการใช้ PNG แต่เราไม่ได้ดำเนินการใดๆ เกี่ยวกับกระบวนการคลายการบีบอัดและการคลายการบีบอัดนี้ กำลังเกิดขึ้นที่ “จุดวาดรูป”! นี่เป็นสถานที่ที่เลวร้ายที่สุดที่เราสามารถฆ่าเวลาได้ - บนเธรดหลัก
มีวิธีบังคับให้คลายการบีบอัด เราสามารถตั้งค่าให้เป็นคุณสมบัติของรูปภาพของ UIImageView ได้ทันที แต่นั่นยังทำให้ภาพในเธรดหลักคลายตัว มีวิธีอื่นที่ดีกว่านี้ไหม?
มีหนึ่ง วาดลงใน CGContext ซึ่งต้องคลายการบีบอัดรูปภาพก่อนจึงจะสามารถวาดได้ เราสามารถทำได้ (โดยใช้ CPU) ในเธรดพื้นหลัง และกำหนดขอบเขตตามความจำเป็นตามขนาดของมุมมองภาพของเรา วิธีนี้จะช่วยเพิ่มประสิทธิภาพกระบวนการวาดภาพของเราโดยทำนอกเธรดหลัก และช่วยเราจากการคำนวณ "เตรียม" ที่ไม่จำเป็นบนเธรดหลัก
ในขณะที่เราอยู่ในนั้น ทำไมไม่เพิ่มเงาเข้าไปในขณะที่เราวาดภาพล่ะ? จากนั้นเราสามารถจับภาพ (และแคชไว้) เป็นภาพคงที่และทึบแสงหนึ่งภาพ รหัสมีดังนี้:
- (UIImage*)generateImageFromName:(NSString*)imageName { //define a boudns for drawing CGRect imgVwBounds = CGRectMake(0, 0, 48, 48); //get the image NSString *filePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"png"]; UIImage *image = [UIImage imageWithContentsOfFile:filePath]; //draw in the context UIGraphicsBeginImageContextWithOptions(imgVwBounds.size, NO, 0); { //get context CGContextRef context = UIGraphicsGetCurrentContext(); //shadow CGContextSetShadowWithColor(context, CGSizeMake(0, 3.0f), 3.0f, [UIColor blackColor].CGColor); CGContextBeginTransparencyLayer (context, NULL); [image drawInRect:imgVwBounds blendMode:kCGBlendModeNormal alpha:1.0f]; CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0); CGContextEndTransparencyLayer(context); } image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }
และในที่สุดก็:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell getCustomCellIdentifier] forIndexPath:indexPath]; // NSInteger index = (indexPath.row % [self.images count]); // NSString *imageName = [self.images objectAtIndex:index]; // // cell.tag = indexPath.row; // // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // NSString *filePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"png"]; // UIImage *image = [UIImage imageWithContentsOfFile:filePath]; // // dispatch_async(dispatch_get_main_queue(), ^{ // if (indexPath.row == cell.tag) { // cell.customCellImageView.image = image; // } // }); // }); cell.customCellImageView.image = [self getImageByIndexPath:indexPath]; cell.customCellImageView.clipsToBounds = YES; // cell.customCellImageView.layer.shadowOffset = CGSizeMake(0, 5); // cell.customCellImageView.layer.shadowOpacity = 0.8f; cell.customCellMainLabel.text = [NSString stringWithFormat:@"Row %li", (long)(indexPath.row + 1)]; cell.customCellMainLabel.layer.shadowOffset = CGSizeMake(0, 3); cell.customCellMainLabel.layer.shadowOpacity = 0.5f; cell.layer.shouldRasterize = YES; cell.layer.rasterizationScale = [UIScreen mainScreen].scale; return cell; }
และผลลัพธ์คือ:
ขณะนี้เรามีค่าเฉลี่ยที่สูงกว่า 55 FPS และการใช้การเรนเดอร์และการใช้ไทเลอร์ของเรานั้นเกือบครึ่งหนึ่งของที่เคยเป็นมา
สรุป
ในกรณีที่คุณสงสัยว่าเราจะทำอะไรได้อีกเพื่อเพิ่มจำนวนเฟรมต่อวินาที ไม่ต้องมองหาที่ไหนอีกแล้ว UILabel ใช้ WebKit HTML เพื่อแสดงข้อความ เราสามารถไปที่ CATextLayer ได้โดยตรงและอาจเล่นกับเงาที่นั่นด้วย
คุณอาจสังเกตเห็นในการใช้งานข้างต้น เราไม่ได้ทำการโหลดรูปภาพในชุดข้อความพื้นหลัง แต่เราแคชไว้แทน เนื่องจากมีเพียง 5 ภาพจึงทำงานได้อย่างรวดเร็วและดูเหมือนจะไม่ส่งผลต่อประสิทธิภาพโดยรวม (โดยเฉพาะอย่างยิ่งเนื่องจากภาพทั้ง 5 ภาพถูกโหลดบนหน้าจอก่อนเลื่อน) แต่คุณอาจต้องการลองย้ายตรรกะนี้ไปยังเธรดพื้นหลังเพื่อประสิทธิภาพเพิ่มเติม
การปรับแต่งประสิทธิภาพคือความแตกต่างระหว่างแอพระดับโลกกับแอพมือสมัครเล่น การปรับประสิทธิภาพให้เหมาะสม โดยเฉพาะอย่างยิ่งเมื่อพูดถึงแอนิเมชั่น iOS อาจเป็นงานที่น่ากลัว แต่ด้วยความช่วยเหลือของ Instruments เราสามารถวินิจฉัยปัญหาคอขวดในการแสดงแอนิเมชั่นบน iOS ได้อย่างง่ายดาย