什么代码必须放在iOS的主线程上?

时间:2013-02-22 23:55:15

标签: ios multithreading performance user-interface grand-central-dispatch

有人可以向我解释一下特别应该在iOS主线程中使用哪些代码行?

我有以下内容:

- (void)asyncWorkOnLayingOutSKUs:(UILongPressGestureRecognizer *)gesture andBlock:(void (^)(BOOL))completion {
__block NSTimeInterval totalTi = -[NSDate timeIntervalSinceReferenceDate];
//
dispatch_queue_t callerQ = dispatch_get_current_queue();
dispatch_queue_t loadingQ = dispatch_queue_create("ff.aq", NULL);
dispatch_async(loadingQ, ^{

    //Code below can be placed here and then I used: dispatch_async(dispatch_get_main_queue(), ^{ //code here that must go on main thread });  in order to make it work

    BOOL trueBool = YES;
    //
    dispatch_async(callerQ, ^{
        totalTi += [NSDate timeIntervalSinceReferenceDate];
        NSLog(@"Performance: %g sec", totalTi);
        completion(trueBool);
        if(completion){

    //For now, there is no multithreading since all code is placed on main thread...

            self.timingDate = [NSDate date];

            switch (self.lastButtonPressedForDragTags) {
                case 1:{
                    self.slotOneButtonIndex = gesture.view.tag-1;
                } break;
                case 2:{
                    self.slotTwoButtonIndex = gesture.view.tag-1;
                } break;
                case 3:{
                    self.slotThreeButtonIndex = gesture.view.tag-1;
                } break;
                case 4:{
                    self.slotFourButtonIndex = gesture.view.tag-1;
                } break;
                case 5:{
                    self.slotFiveButtonIndex = gesture.view.tag-1;
                } break;
            }

            int touchedtag = gesture.view.tag;
            UIView* thisView;
            UIScrollView* thisScrollView;

            switch (gesture.view.superview.superview.tag) {
                case -1:{
                    thisView = self.dragTagsScrollViewContainer;
                    thisScrollView = self.dragTagsScrollView;
                } break;
                case -2:{
                    thisView = self.SKUTagsScrollViewContainer;
                    thisScrollView = self.SKUTagsScrollView;
                } break;
                case -3:{
                    thisView = self.otherTagsScrollViewContainer;
                    thisScrollView = self.otherTagsScrollView;
                } break;
                case -4:{
                    thisView = self.leftDraggedTagsScrollViewContainer;
                    thisScrollView = self.leftDraggedTagsScrollView;
                } break;
            }

            UIButton *button = (UIButton*)[thisView viewWithTag:touchedtag];

            if(![button isSelected]){
                [self setButtonSelected:button];
            }
            else{
                [self setButtonDeselected:button];
            }

            //Get the position of the button RELATIVE to the superview by subtracting the content offset for x direction:
            CGRect buttonPosition = CGRectMake(button.frame.origin.x - thisScrollView.contentOffset.x, button.frame.origin.y, button.frame.size.width, button.frame.size.height);

            CGRect sizeOfScrollView = CGRectMake(0, 0, thisScrollView.frame.size.width, thisScrollView.frame.size.height);

            if(CGRectContainsRect(sizeOfScrollView, buttonPosition)){}
            else{
                //extend scrollview to L or R
                CGPoint rightBottomEdgeOfButton = CGPointMake(buttonPosition.origin.x + buttonPosition.size.width, buttonPosition.origin.y + buttonPosition.size.height);
                CGRect intersection = CGRectIntersection(sizeOfScrollView, buttonPosition);
                float amountToMove;
                float currentXDirectionOffset = thisScrollView.contentOffset.x;
                CGPoint newOffset;

                if (CGRectContainsPoint(sizeOfScrollView, rightBottomEdgeOfButton)){
                    //Need to move Left
                    amountToMove = button.frame.size.width - intersection.size.width + 5;
                }
                else{
                    //Need to move Right
                    amountToMove = -(button.frame.size.width - intersection.size.width + 5);
                }
                newOffset = CGPointMake(currentXDirectionOffset-amountToMove, thisScrollView.contentOffset.y);

                [thisScrollView setContentOffset:newOffset];
            }

            [self.SKUTagsScrollViewContainer removeFromSuperview];

            NSMutableArray* arrayToUse = [[NSMutableArray alloc]initWithArray:[self getIntersectionArray]];


            [self populateScrollViewWithArray:arrayToUse andScrollView:self.SKUTagsScrollView withContainer:self.SKUTagsScrollViewContainer andmaxNumberOfRowsForScrollView:6];

#ifdef DEBUG
            NSLog(@"Time taken to AFTER populateScrollViewWithArray: %g and for %d controls", [[NSDate date] timeIntervalSinceDate:self.timingDate], [arrayToUse count]);
#endif
        }
    });
});
dispatch_release(loadingQ);
}

现在显然加载q没有任何内容,方法populateScrollViewWithArray基本上在UIScrollView上排列了一些按钮。我发现我能够包装'我认为对dispatch_async(dispatch_get_main_queue(), ^{});中的ui渲染至关重要的任何代码,然后我可以将if(completion){}中显示的所有代码放在加载q上。

这两个选项都有效,我测量了性能,发现我得到了类似的结果。

我的问题是我是iOS的新手,我想知道:

如何使用线程最大化性能?

究竟应该在主线程上进行什么?在方法populateScrollViewWithArray中,我执行了以下操作:

            dispatch_async(dispatch_get_main_queue(), ^{

            [scrollview addSubview:scrollViewContainer];
            [self.view addSubview:scrollview];
        });

但我做了一些事情,比如创建标签,创建按钮并在加载q上添加手势(没有在dispatch_async(dispatch_get_main_queue(), ^{});

中明确地将它们包围起来

除了阅读文档'以外,还有人能给我一个简明的解释吗?因为我经常发现Apple文档很长时间。有点像这个问题......

1 个答案:

答案 0 :(得分:2)

一般规则是大多数UIKit类在后台线程中使用是不安全的,除非文档明确说明(有时文档不一致,有时它会中断,例如UIKit绘图应该是线程安全的,因为4.0但在iOS 5.x中被破坏了。

IIRC文档还建议在后台线程上创建视图(例如通过加载笔尖)是安全的,只要它们没有被添加到窗口中。尽管如此,我还没有对此进行浸泡测试,因此可能存在一些不安全的边缘情况(UIWebView可能就是其中之一)。

它还稍微更复杂,因为根据GKTapper example code,某些UIKit类对后台线程的发布不安全:

  

如果在辅助队列上执行的块中视图控制器引用,   视图控制器可以在主队列之外被释放(并且dealloc&#d; d)。 即使在主线程上安排了实际的块,也是如此。 ... UIKit视图控制器只能在主线程上访问,因此上面的代码片段可能导致微妙和困难追踪错误

您的代码在后台线程上运行的块(由a引用的块)中捕获selfgesture,这意味着它们可能会在后台线程上释放,这可能偶尔会崩溃。< / p>

其他挑剔:

  • totalTi不需要是__block变量。
  • loadingQ应替换为全局队列。
  • if(completion)始终为true(如果completion(trueBool)为NULL,则上面一行中出现completion。)
  • 您可以将大部分代码移到VC中的完成方法,而不是有6个级别的缩进。