[iOS] CPU 리소스 소모 원인 및 솔루션[전재]

5331 단어
다음으로 이동:http://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/

객체 작성


대상의 창설은 메모리를 분배하고 속성을 조정하며 심지어 파일을 읽는 작업까지 하여 CPU 자원을 비교적 소모한다.가급적 무게의 대상을 가벼운 대상으로 대체하면 성능을 최적화할 수 있다.예를 들어 CALayer가 UIView보다 훨씬 가볍기 때문에 터치 이벤트에 응답하는 컨트롤러가 필요하지 않고 CALayer로 표시하는 것이 더욱 적합하다.객체가 UI 작업과 관련이 없는 경우 가능한 백엔드 스레드로 만들려고 하지만 안타깝게도 CALayer가 포함된 컨트롤은 마스터 스레드에서만 만들고 조작할 수 있습니다.Storyboard를 통해 보기 대상을 만들 때 그 자원 소모는 코드를 통해 직접 대상을 만드는 것보다 매우 크고 매우 많다. 성능이 민감한 인터페이스에서 Storyboard는 좋은 기술 선택이 아니다.대상의 창설 시간을 최대한 늦추고 대상의 창설을 여러 작업으로 분산시킨다.비록 이것은 실현하기가 비교적 번거롭고 가져오는 장점이 많지 않지만, 할 능력이 있다면 가능한 한 시도해 보아야 한다.만약 대상이 복용할 수 있고, 복용의 대가가 방출, 새 대상을 만드는 것보다 적다면, 이러한 대상은 가능한 한 캐시 탱크에 복용해야 한다.

객체 조정


대상의 조정도 자주 CPU 자원을 소모하는 곳이다.특히 CALayer: CALayer 내부에는 속성이 없습니다. 속성 방법을 호출할 때 내부는 실행할 때 ResolveInstanceMethod를 대상으로 임시로 방법을 추가하고 대응하는 속성 값을 내부의 Dictionary에 저장합니다. 또한delegate를 알리고 애니메이션을 만드는 등 자원을 많이 소모합니다.UIView의 디스플레이와 관련된 속성(예를 들어 프레임/bounds/transform) 등은 사실상 모두 CALayer 속성이 비치기 때문에 UIView의 이러한 속성을 조정할 때 소모되는 자원은 일반적인 속성보다 훨씬 크다.이에 대해 응용 프로그램에서 불필요한 속성 수정을 최대한 줄여야 한다.뷰 계층을 조정할 때 UIView, CALayer 간에 호출과 알림이 많이 발생하기 때문에 성능을 최적화할 때 뷰 계층을 조정하고 뷰를 추가하고 제거하는 것을 최대한 피해야 한다.

객체 제거


대상의 폐기는 자원을 많이 소모하지는 않지만 누적하는 것도 무시할 수 없다.일반적으로 용기류가 대량의 대상을 가지고 있을 때 소각할 때의 자원 소모는 매우 뚜렷하다.마찬가지로 만약 대상이 백엔드 라인에 놓아서 방출할 수 있다면 백엔드 라인으로 옮겨라.여기에 작은 Tip이 있습니다. 대상을 Block에 포획한 다음 백엔드 대기열에 던져서 컴파일러 경고를 피하기 위해 메시지를 보내면 백엔드 라인에서 대상을 삭제할 수 있습니다.
4
  • 테스트용 코드(동시 방출 시간 소모)
  •  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            NSMutableArray *t_list = [[NSMutableArray alloc] init];
            for (int i = 0; i < 9999999 * 3; i++) {
                TestObject *ob = [[TestObject alloc] init];
                [t_list addObject:ob];
            }
            NSDate *s_date = [NSDate date];
            t_list = nil;
            NSDate *e_date = [NSDate date];
            NSLog(@" :%d ",(int)[e_date timeIntervalSinceDate:s_date]);
        });
    

    4
  • 수정된 코드(비동기 방출 시간 소모)
  •   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            NSMutableArray *t_list = [[NSMutableArray alloc] init];
            for (int i = 0; i < 9999999 * 3; i++) {
                TestObject *ob = [[TestObject alloc] init];
                [t_list addObject:ob];
            }
            
            NSMutableArray *m_list = t_list;
            NSDate *s_date = [NSDate date];
            t_list = nil;
       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                [m_list class];
            });
            
            NSDate *e_date = [NSDate date];
            
            NSLog(@" :%d ",(int)[e_date timeIntervalSinceDate:s_date]);
        });
    

    레이아웃 계산


    보기 레이아웃의 계산은 앱에서 가장 흔히 볼 수 있는 CPU 자원을 소모하는 곳이다.만약 백엔드 라인에서 보기 레이아웃을 미리 계산하고 보기 레이아웃을 캐시할 수 있다면 이곳은 기본적으로 성능 문제가 발생하지 않을 것이다.뷰 레이아웃은 어떤 기술을 사용하든 UIView에 적용됩니다.frame/bounds/center 등 속성 조정에 있어위에서 말한 바와 같이 이러한 속성에 대한 조정은 자원을 매우 소모하기 때문에 가능한 한 레이아웃을 미리 계산하고 필요할 때 대응하는 속성을 한꺼번에 조정하며 여러 번, 빈번하게 이런 속성을 계산하고 조정하지 않는다.

    Autolayout


    Autolayout은 애플 자체가 제창한 기술로 대부분의 상황에서도 개발 효율을 높일 수 있지만 Autolayout은 복잡한 보기에 심각한 성능 문제가 자주 발생한다.뷰 수가 증가함에 따라 Autolayout에 따른 CPU 소모는 기하급수적으로 상승할 것이다.구체적인 데이터는 이 글을 볼 수 있다.http://pilky.me/36/. 프레임 등의 속성을 수동으로 조정하고 싶지 않으면, 예를 들어 흔히 볼 수 있는 left/right/top/bottom/width/height 단축 속성 등을ComponentKit,AsyncDisplayKit 등 프레임워크로 대체할 수 있습니다.

    텍스트 계산


    만약에 한 인터페이스에 대량의 텍스트(예를 들어 웨이보 위챗 모멘트 등)가 포함된다면 텍스트의 넓이와 높은 계산은 많은 자원을 차지하고 피할 수 없을 것이다.텍스트 표시에 특별한 요구가 없다면, UILAbel 내부의 실현 방식을 참고하십시오. [NSAttributed String bounding RectWith Size: options:context:] 로 텍스트의 폭을 계산하고, - [NSAttributed String draw With Rect: options:context:] 로 텍스트를 그릴 수 있습니다.비록 이 두 가지 방법은 성능이 좋지만, 여전히 메인 라인이 막히지 않도록 백엔드 라인에 놓아야 한다.만약 당신이 CoreText로 텍스트를 그린다면, CoreText 레이아웃 대상이 되어 스스로 계산할 수 있고, CoreText 대상은 나중에 그림을 그릴 때 사용할 수 있도록 보존할 수 있습니다.

    텍스트 렌더링


    화면에 보이는 모든 텍스트 내용 컨트롤, UIWebView를 포함하여, 밑바닥은 CoreText를 통해 조판되고, 비트맵으로 그려져 있습니다.일반적인 텍스트 컨트롤(UIlabel, UITExtView 등)은 레이아웃과 그리기가 주 스레드에서 이루어지기 때문에 대량의 텍스트를 표시할 때 CPU의 압력이 매우 크다.이 해결 방안은 사용자 정의 텍스트 컨트롤로 TextKit나 맨 밑에 있는CoreText로 텍스트를 비동기적으로 그리는 것입니다.비록 이것은 실현하기 매우 번거롭지만 그 장점도 매우 크다. 코어텍스 대상이 만들어진 후에 텍스트의 넓고 높은 정보를 직접 얻을 수 있어 여러 번의 계산을 피할 수 있다(UIlabel 대시간 계산, UIlabel 그리기 시 내부 재계산).CoreText 객체는 나중에 여러 번 렌더링할 수 있도록 메모리를 적게 사용합니다.

    그림의 디코딩


    UIImage나 CGImageSource의 몇 가지 방법으로 그림을 만들 때, 그림 데이터는 바로 디코딩되지 않습니다.그림을 UIImageView 또는 CALayer에 설정합니다.콘텐츠에서 가고 CALayer가 GPU에 제출되기 전에 CGImage의 데이터가 디코딩됩니다.이 단계는 주선에서 발생했을 뿐만 아니라 피할 수 없다.이 메커니즘을 돌리려면, 흔히 볼 수 있는 방법은 백엔드 라인에서 먼저 그림을 CG Bitmap Context에 그린 다음 Bitmap에서 직접 그림을 만드는 것입니다.현재 흔히 볼 수 있는 인터넷 사진 라이브러리는 모두 이 기능을 자체로 가지고 있다.

    그림 그리기


    그림의 그리기는 보통 CG로 시작하는 방법으로 그림을 캔버스에 그려서 캔버스에서 그림을 만들고 이런 과정을 보여주는 것을 말한다.여기서 가장 흔히 볼 수 있는 곳은 [UIView draw Rect:] 안입니다.CoreGraphic 방법은 일반적으로 모두 라인이 안전하기 때문에 그림의 그리기는 백엔드 라인에 쉽게 놓을 수 있다.간단하게 비동기적으로 그리는 과정은 대체로 다음과 같다(실제 상황은 이보다 훨씬 복잡하지만 원리는 기본적으로 일치한다)
    - (void)display {
        dispatch_async(backgroundQueue, ^{
            CGContextRef ctx = CGBitmapContextCreate(...);
            // draw in context...
            CGImageRef img = CGBitmapContextCreateImage(ctx);
            CFRelease(ctx);
            dispatch_async(mainQueue, ^{
                layer.contents = img;
            });
        });
    }
    

    좋은 웹페이지 즐겨찾기