1

有人可以向我解释一下哪些代码行应该放在 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 上安排了一些按钮。我发现我能够“包装”任何我认为对其中的 ui 渲染至关重要dispatch_async(dispatch_get_main_queue(), ^{});的代码,然后我可以将此处显示的所有代码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 文档很冗长。有点像这个问题......

4

1 回答 1

2

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

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

它也比这稍微复杂一些,因为根据GKTapper 示例代码,一些 UIKit 类从后台线程释放是不安全的:

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

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

其他挑剔:

  • totalTi不需要是__block变量。
  • loadingQ应替换为全局队列。
  • if(completion)始终为真(在上面的行中,如果为 NULL completion(trueBool),则崩溃)。completion
  • 您可以将大部分代码移动到 VC 中的完成方法,而不是使用 6 级缩进。
于 2013-02-23T00:15:11.850 回答