我想在一个应用程序中拥有大约400个CALayers,并在动画手势中重新定位(并缩放它们)。在此操作过程中,他们无需重新绘制内容,只需移动并重新缩放即可。我想以每秒60帧的速度保持动画效果。
我的目标是至少可以运行iOS 7的任何东西。支持iOS 6设备也很不错。
为了检查可行性,我编写了一个基于Apple的Single View模板项目的小型测试应用程序。代码包含在下面。
使用仪器进行性能分析,绝大部分运行时间(94%)都在显示链接回调中。其中54%用于[CALayer setPosition]
,33%用于CA::Transaction::commit()
。我已经包含了一个显示此内容的个人资料。
许多位置设置配置文件跟踪似乎是关于做很多我不需要框架做的事情。所以,有没有人知道是否有某种方法可以关闭所有这些东西并让图层变得更加愚蠢:-)即 - 如果我只是想让自己动画一下,基本上可以启用某种快速路径?
作为一个小注释,我已经提到了一种提到here的技术,通过为位置键的更改设置空操作来提高速度。
如果您对加速有其他想法(比如使用UIViews代替CALayers),我很乐意听到它们。
谢谢!
我希望,相关部分的概况:
Running Time Self Symbol Name
147914.0ms 100.0% 1754.0 -[STViewController displayLinkCallback:]
85265.0ms 57.6% 323.0 -[CALayer setPosition:]
83842.0ms 56.6% 8301.0 CA::Layer::set_position(CA::Vec2<double> const&, bool)
27247.0ms 18.4% 811.0 CA::Layer::begin_change(CA::Transaction*, unsigned int, objc_object*&)
21103.0ms 14.2% 316.0 actionForKey(CALayer*, CA::Transaction*, NSString*)
4819.0ms 3.2% 2296.0 CAAtomGetString
188.0ms 0.1% 188.0 -[CALayer actionForKey:]
171.0ms 0.1% 171.0 DYLD-STUB$$x_strtod
88.0ms 0.0% 88.0 DYLD-STUB$$-[NSString(CAMLWriter) CAMLType]
34.0ms 0.0% 34.0 CA::Transaction::get_value(unsigned int, _CAValueType, void*)
33.0ms 0.0% 33.0 DYLD-STUB$$-[NSNumber(CAMLWriter) CAMLType]
23316.0ms 15.7% 730.0 CA::Layer::end_change(CA::Transaction*, unsigned int, objc_object*)
12759.0ms 8.6% 1266.0 CA::Layer::writable_state(CA::Transaction*)
7523.0ms 5.0% 557.0 -[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:]
1145.0ms 0.7% 1145.0 OSSpinLockLock
594.0ms 0.4% 594.0 _Unwind_SjLj_Unregister
327.0ms 0.2% 327.0 OSSpinLockLock$shim
310.0ms 0.2% 310.0 CAAtomGetString
306.0ms 0.2% 306.0 __inline_isnand
298.0ms 0.2% 246.0 CA::Transaction::unlock()
215.0ms 0.1% 215.0 objc_msgSend
203.0ms 0.1% 203.0 _Unwind_SjLj_Register
168.0ms 0.1% 168.0 -[CATextLayer didChangeValueForKey:]
149.0ms 0.1% 149.0 DYLD-STUB$$-[NSNumber(CAMLWriter) CAMLType]
141.0ms 0.0% 141.0 CA::Layer::property_did_change(CA::Transaction*, unsigned int)
129.0ms 0.0% 129.0 CFRetain
123.0ms 0.0% 123.0 CA::Transaction::lock()
112.0ms 0.0% 112.0 CA::Transaction::ensure_compat()
100.0ms 0.0% 100.0 _NSKeyValueRetainedObservationInfoForObject
89.0ms 0.0% 89.0 objc_msgSend$shim
78.0ms 0.0% 78.0 DYLD-STUB$$-[NSURL(CAMLWriter) encodeWithCAMLWriter:]
57.0ms 0.0% 57.0 DYLD-STUB$$CAMLWriterFreeAttributeList(_CAMLWriterAttribute*)
42.0ms 0.0% 42.0 actionForKey(CALayer*, CA::Transaction*, NSString*)
30.0ms 0.0% 30.0 DYLD-STUB$$-[CAShapeLayer setFillColor:]
25.0ms 0.0% 25.0 DYLD-STUB$$x_strtod
24.0ms 0.0% 24.0 DYLD-STUB$$CFNumberFormatterSetProperty$shim
23.0ms 0.0% 23.0 _CFRetain
8.0ms 0.0% 8.0 magazine_alloc_
410.0ms 0.2% 410.0 DYLD-STUB$$x_strtod
135.0ms 0.0% 135.0 CA::Layer::begin_change(CA::Transaction*, unsigned int, objc_object*&)
133.0ms 0.0% 133.0 -[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:]
107.0ms 0.0% 107.0 CA::Transaction::unlock()
74.0ms 0.0% 74.0 DYLD-STUB$$-[NSURL(CAMLWriter) encodeWithCAMLWriter:]
60.0ms 0.0% 60.0 DYLD-STUB$$CAMLWriterFreeAttributeList(_CAMLWriterAttribute*)
53.0ms 0.0% 53.0 CA::Layer::writable_state(CA::Transaction*)
53.0ms 0.0% 53.0 DYLD-STUB$$-[NSString(CAMLWriter) CAMLType]
27.0ms 0.0% 27.0 CA::Layer::end_change(CA::Transaction*, unsigned int, objc_object*)
27.0ms 0.0% 27.0 CA::Transaction::ensure_compat()
21.0ms 0.0% 21.0 DYLD-STUB$$CA::Display::DisplayLinkItem::set_user_info(void const*)
52572.0ms 35.5% 46.0 CA::Transaction::commit()
38450.0ms 25.9% 185.0 CA::Context::commit_transaction(CA::Transaction*)
33772.0ms 22.8% 75.0 CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*)
33649.0ms 22.7% 36.0 CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*)
33522.0ms 22.6% 458.0 CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*)
32936.0ms 22.2% 3694.0 CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*)
28899.0ms 19.5% 298.0 CA::Context::commit_layer(CA::Layer*, unsigned int, unsigned int, void*)
84.0ms 0.0% 84.0 CA::Render::Object::unref() const
83.0ms 0.0% 83.0 CA::Render::encode_set_object(CA::Render::Encoder*, unsigned long, unsigned int, CA::Render::Object*, unsigned int)
54.0ms 0.0% 54.0 DYLD-STUB$$x_strtod
47.0ms 0.0% 47.0 CA::Render::Encoder::encode_int32(unsigned int)
36.0ms 0.0% 36.0 CA::Render::Layer::~Layer()
22.0ms 0.0% 21.0 CA::Layer::thread_flags_(CA::Transaction*)
17.0ms 0.0% 17.0 CA::Layer::copy_render_layer(CA::Transaction*, unsigned int, unsigned int*)
128.0ms 0.0% 48.0 CA::Context::commit_layer(CA::Layer*, unsigned int, unsigned int, void*)
91.0ms 0.0% 0.0 CA::Context::commit_layer(CA::Layer*, unsigned int, unsigned int, void*)
46.0ms 0.0% 0.0 CA::Context::commit_layer(CA::Layer*, unsigned int, unsigned int, void*)
1.0ms 0.0% 1.0 CA::Render::Object::unref() const
1.0ms 0.0% 1.0 CA::Render::Encoder::encode_int32(unsigned int)
1765.0ms 1.1% 61.0 CA::Render::Encoder::send_message(unsigned int, unsigned int)
904.0ms 0.6% 15.0 _dispatch_queue_wakeup_global_slow
711.0ms 0.4% 3.0 CA::Render::Encoder::~Encoder()
100.0ms 0.0% 1.0 CA::Layer::layout_and_display_if_needed(CA::Transaction*)
97.0ms 0.0% 13.0 pthread_mutex_unlock
87.0ms 0.0% 14.0 _dispatch_async_f_slow
85.0ms 0.0% 41.0 _pthread_mutex_lock
74.0ms 0.0% 2.0 CA::Transaction::foreach_root(void (*)(CA::Layer*, void*), void*)
70.0ms 0.0% 29.0 CA::Layer::prepare_commit(CA::Transaction*)
66.0ms 0.0% 18.0 CA::Transaction::get_value(unsigned int, _CAValueType, void*)
56.0ms 0.0% 31.0 CA::Render::Encoder::ObjectCache::encode_invalidations(CA::Render::Encoder*)
51.0ms 0.0% 0.0 CA::Layer::collect_animations(CA::Transaction*, double, double*)
38.0ms 0.0% 38.0 dispatch_async_f
38.0ms 0.0% 38.0 _dispatch_queue_push_slow
34.0ms 0.0% 32.0 CA::Context::retain_all_contexts(bool, CA::Context**, unsigned long&)
33.0ms 0.0% 12.0 CA::Render::Encoder::Encoder(x_heap_struct*, unsigned int, void*, unsigned int, double)
31.0ms 0.0% 16.0 CA::Context::unref(bool)
29.0ms 0.0% 29.0 CA::Layer::set_next_animation_time(CA::Transaction*, double, double)
26.0ms 0.0% 20.0 CA::Transaction::unlock()
20.0ms 0.0% 20.0 x_heap_malloc_small_
19.0ms 0.0% 19.0 CA::DispatchGroup::enqueue(void (*)(void*), void*)
17.0ms 0.0% 17.0 CARecordTransaction
17.0ms 0.0% 17.0 CABackingStoreCollectAsync
15.0ms 0.0% 15.0 CAMarkStatistic
15.0ms 0.0% 15.0 CALayerGetLayer
13.0ms 0.0% 13.0 x_mem_dealloc_chain
10.0ms 0.0% 10.0 CA::Transaction::lock()
8.0ms 0.0% 8.0 x_heap_new_with_ptr
7.0ms 0.0% 7.0 x_heap_free
6.0ms 0.0% 6.0 DYLD-STUB$$-[NSString(CAMLWriter) CAMLType]
5.0ms 0.0% 5.0 CA::Transaction::foreach_command(unsigned int, void (*)(int, unsigned long, void const*, void*), void*)
5.0ms 0.0% 4.0 CATimeWithHostTime
4.0ms 0.0% 4.0 DYLD-STUB$$-[OS_dispatch_data finalize]
4.0ms 0.0% 4.0 DYLD-STUB$$-[NSNumber(CAMLWriter) CAMLType]
3.0ms 0.0% 3.0 OSSpinLockUnlock$shim
3.0ms 0.0% 3.0 OSSpinLockLock$shim
3.0ms 0.0% 3.0 pthread_mutex_lock
3.0ms 0.0% 3.0 DYLD-STUB$$dlsym
2.0ms 0.0% 2.0 CA::CG::Queue::collect(double)
2.0ms 0.0% 0.0 CACurrentMediaTime
2.0ms 0.0% 2.0 CA::Render::Encoder::set_object_cache(CA::Render::Encoder::ObjectCache*)
2.0ms 0.0% 2.0 x_heap_malloc
2.0ms 0.0% 2.0 __mtx_droplock
1.0ms 0.0% 1.0 OSSpinLockLock
1.0ms 0.0% 1.0 CA::Render::Encoder::encode_int64(unsigned long long)
1.0ms 0.0% 1.0 CA::Layer::collect_layers_(CA::Layer::CollectLayersData*)
1.0ms 0.0% 1.0 CA::Render::Coder::Coder(x_heap_struct*)
1.0ms 0.0% 1.0 DYLD-STUB$$CAMLWriterFreeAttributeList(_CAMLWriterAttribute*)
1.0ms 0.0% 1.0 x_hash_table_foreach
1.0ms 0.0% 1.0 CA::Render::Coder::~Coder()
1.0ms 0.0% 1.0 pthread_threadid_np
13744.0ms 9.2% 2112.0 CA::Layer::free_transaction(CA::Transaction*)
70.0ms 0.0% 26.0 x_hash_table_free
67.0ms 0.0% 0.0 CA::Transaction::Level::free_levels(CA::Transaction::Level*)
34.0ms 0.0% 12.0 x_hash_table_foreach
30.0ms 0.0% 30.0 CALayerRelease
29.0ms 0.0% 29.0 CA::Layer::State::unref(CA::Transaction*) const
25.0ms 0.0% 20.0 x_hash_table_remove_if
16.0ms 0.0% 16.0 x_hash_table_size
11.0ms 0.0% 11.0 DYLD-STUB$$__39-[CAWindowServer displayWithDisplayId:]_block_invoke
8.0ms 0.0% 8.0 DYLD-STUB$$-[NSString(CAMLWriter) CAMLType]
7.0ms 0.0% 7.0 x_mem_dealloc_size
4.0ms 0.0% 4.0 OSSpinLockLock
4.0ms 0.0% 4.0 CA::Render::Encoder::ObjectCache::encode_invalidations(CA::Render::Encoder*)
3.0ms 0.0% 3.0 DYLD-STUB$$usleep$shim
3.0ms 0.0% 0.0 magazine_dealloc_
2.0ms 0.0% 2.0 DYLD-STUB$$-[NSNumber(CAMLWriter) CAMLType]
2.0ms 0.0% 2.0 x_heap_malloc
2.0ms 0.0% 2.0 x_heap_free
2.0ms 0.0% 2.0 CA::Render::Encoder::~Encoder()
2.0ms 0.0% 2.0 DYLD-STUB$$mig_put_reply_port$shim
2.0ms 0.0% 2.0 CA::release_root_if_unused(CA::Layer*, CA::Layer*, void*)
2.0ms 0.0% 2.0 CA::Render::Encoder::send_message(unsigned int, unsigned int)
1.0ms 0.0% 1.0 CAMarkStatistic
1.0ms 0.0% 1.0 CA::Layer::collect_animations(CA::Transaction*, double, double*)
1.0ms 0.0% 1.0 pthread_mutex_unlock
1.0ms 0.0% 1.0 CA::release_root(CA::Layer*, CA::Layer*, void*)
1.0ms 0.0% 1.0 x_heap_malloc_small_
1.0ms 0.0% 1.0 _pthread_mutex_lock
1.0ms 0.0% 1.0 x_mem_dealloc_chain
4061.0ms 2.7% 4061.0 cos
2317.0ms 1.5% 2317.0 objc_msgSend
1155.0ms 0.7% 1155.0 sin
144.0ms 0.0% 0.0 +[CATransaction setAnimationDuration:]
104.0ms 0.0% 24.0 objc_object::sidetable_release(bool)
92.0ms 0.0% 78.0 CA::Transaction::push()
86.0ms 0.0% 17.0 objc_object::sidetable_retain()
76.0ms 0.0% 76.0 objc_retain
59.0ms 0.0% 59.0 -[__NSArrayM countByEnumeratingWithState:objects:count:]
56.0ms 0.0% 56.0 CA::Layer::set_position(CA::Vec2<double> const&, bool)
42.0ms 0.0% 42.0 objc_release
41.0ms 0.0% 41.0 DYLD-STUB$$objc::DenseMapBase<objc::DenseMap<objc_object*, unsigned long, true, objc::DenseMapInfo<objc_object*> >, objc_object*, unsigned long, objc::DenseMapInfo<objc_object*>, true>::erase(objc_object* const&)
19.0ms 0.0% 19.0 CA::Transaction::pop()
17.0ms 0.0% 17.0 +[CATransaction begin]
17.0ms 0.0% 13.0 CATimeWithHostTime
15.0ms 0.0% 15.0 -[CADisplayLink timestamp]
10.0ms 0.0% 10.0 +[CATransaction commit]
4.0ms 0.0% 4.0 os_lock_trylock
2.0ms 0.0% 2.0 CA::Transaction::ensure_compat()
1.0ms 0.0% 1.0 x_hash_table_remove_if
1.0ms 0.0% 1.0 DYLD-STUB$$-[NSString(CAMLWriter) CAMLType]
1.0ms 0.0% 1.0 DYLD-STUB$$-[NSNumber(CAMLWriter) CAMLType]
1.0ms 0.0% 1.0 x_heap_free
1.0ms 0.0% 1.0 x_hash_table_free
1.0ms 0.0% 1.0 objc::DenseMapBase<objc::DenseMap<objc_object*, unsigned long, true, objc::DenseMapInfo<objc_object*> >, objc_object*, unsigned long, objc::DenseMapInfo<objc_object*>, true>::find(objc_object* const&)
这是我在模板Single View Application项目上添加的代码:
// STViewController.m
// LayerSpeedTest
#import "STViewController.h"
@implementation CALayer (Extensions)
- (void)setNullAsActionForKeys:(NSArray *)keys
{
NSMutableDictionary *dict = [self.actions mutableCopy];
if(dict == nil)
{
dict = [NSMutableDictionary dictionaryWithCapacity:[keys count]];
}
for(NSString *key in keys)
{
[dict setObject:[NSNull null] forKey:key];
}
self.actions = dict;
}
@end
@interface STViewController ()
@property (nonatomic, retain) NSArray *layers;
@property (nonatomic, retain) CADisplayLink *displayLink;
-(void) displayLinkCallback:(CADisplayLink*) displayLink;
@end
@implementation STViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *const view = [self view];
[view setBackgroundColor: [UIColor blackColor]];
CALayer *const layer = [view layer];
NSMutableArray *layers = [NSMutableArray array];
for(NSUInteger i=0; i<24*3*4; ++i)
{
CATextLayer *const textLayer = [CATextLayer layer];
[textLayer setContentsScale: [[UIScreen mainScreen] scale]];
[textLayer setForegroundColor: [[UIColor whiteColor] CGColor]];
[textLayer setBackgroundColor: [[UIColor clearColor] CGColor]];
[textLayer setBounds: CGRectMake(0,0,100,100)];
[textLayer setString: @"100"];
[textLayer setNeedsDisplay];
[textLayer setPosition: CGPointMake(160,200)];
[textLayer setNullAsActionForKeys: @[@"position"]];
[layer addSublayer: textLayer];
[layers addObject: textLayer];
}
_layers = layers;
_displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(displayLinkCallback:)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
-(void) displayLinkCallback:(CADisplayLink *)displayLink
{
const NSTimeInterval timestamp = [displayLink timestamp]*0.3;
[CATransaction begin];
[CATransaction setAnimationDuration:0];
{
NSUInteger i=0;
for(CALayer *const layer in _layers)
{
const CGFloat angle = i*0.1 + timestamp;
[layer setPosition: CGPointMake( 160 + 100 * sin(angle), 250 + 200 * cos(angle*0.728734))];
++i;
}
}
[CATransaction commit];
}
@end
答案 0 :(得分:0)
我真的认为你应该使用Sprite Kit,OpenGLES或任何其他东西来动画400项并保持60 fps。 BTW,60fps太多了 - 甚至Xcode要求我在我的测试中从60减少到30。
假设您无法从CoreAnimation更改为更高效的工具,那么您对使用“手动”动画的iOS感到非常生气。而不是手动设置位置,使用KeyPath:
如果您想,您必须手动设置位置,您可能需要将此“随机”运动转换为等式。 CA事务管理自己“更聪明”,而不是每秒进行60次事务。
而且,作为最后的提醒,你需要让你的模型更简单。使用CATextLayer是一种浪费CPU周期的恶意方法 - 特别是如果您打算使用缩放。您可以通过预渲染到CGImage来挤压一些FPS。
但严重的是,考虑不使用CoreAnimation:60 FPS意味着你只有16ms的CPU时间来计算下一帧,构建场景以及渲染这些东西所需的所有mumbo-jumbo。全部只使用一个线程(CA不是线程安全的)。使用GL或SK可以利用iOS设备的实际功能,通过在GPU中完成大量工作。