且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用“dispatch_async(dispatch_get_main_queue(),^ {block})更新UI

更新时间:2023-10-06 13:08:28

a href =http://***.com/questions/28177978/updating-the-ui-using-dispatch-asyncdispatch-get-main-queue-block#comment44725392_28177978> @rmaddy 说: p>


线程注意事项



对应用程序用户界面的操作必须在主线程上进行。因此,应该始终从应用程序的主线程中运行的代码调用UIView类的方法。只有在创建视图对象本身时,所有其他操作都应该在主线程上进行。


- UIView类参考



从这个引用推导出以下规则:




  • 继承自

  • 初始化方法( -init * / $>
  • 任何方法都可以从任何线程的UIView子类中调用UIKit类的实例。
  • li>


这里是我感觉舒服的代码:

 code> UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(100,200,150,150)]; // OK因为-initWithFrame:是初始化
UIColor * color = [UIColor greenColor]; // OK因为UIColor不是UIView的子类
NSString * text = jsonDict [@object_or_array];

dispatch_async(dispatch_get_main_queue(),^ {
label.backgroundColor = color;
label.text = text;
[self.view addSubview:label];
});

但实际上,最安全的是从主线程做这些:



dispatch_async(dispatch_get_main_queue(),^ {
UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(100,200,150,150 )];
label.backgroundColor = [UIColor greenColor];
label.text = jsonDict [@object_or_array];
[self.view addSubview:label];
} );

除非遇到性能问题,否则没有理由。


I wanted to update the UI in the NSURLSession's completion block. The initial implementation didn't update the UI immediately. It updated the UI maybe 20 seconds later. Here is the initial implementation.

  NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 200, 150, 150)];
        label.backgroundColor = [UIColor greenColor];
        label.text = jsonDict[@"object_or_array"];

        dispatch_async(dispatch_get_main_queue(), ^{ 
            [self.view addSubview:label];
        });
    }];

    [task resume];

I moved the location of the label.text = jsonDict[@"object_or_array"] inside the main queue as following.

 dispatch_async(dispatch_get_main_queue(), ^{ 
                label.text = jsonDict[@"object_or_array"]
                [self.view addSubview:label];
            });

Then the UI was updated immediately as expected.

Could anyone tell me why this is?

As @rmaddy has said:

Threading Considerations

Manipulations to your application’s user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your application. The only time this may not be strictly necessary is when creating the view object itself but all other manipulations should occur on the main thread.

UIView Class Reference

From this quote, I infer the following rules:

  • Instances of classes that inherit from UIView should only have methods called on the main thread (including property getters and setters).
  • Initialization methods (-init*) of UIKit objects can be done in any thread.
  • Any method can be called on instances of UIKit classes not sub-classed from UIView from any thread.

Here is the code I would feel comfortable with:

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 200, 150, 150)]; // OK because -initWithFrame: is initialization
UIColor *color = [UIColor greenColor]; // OK because UIColor is not a subclass of UIView
NSString *text = jsonDict[@"object_or_array"];

dispatch_async(dispatch_get_main_queue(), ^{ 
    label.backgroundColor = color;
    label.text = text;
    [self.view addSubview:label];
});

But really, the safest is to do it all from the main thread:

dispatch_async(dispatch_get_main_queue(), ^{ 
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 200, 150, 150)];
    label.backgroundColor = [UIColor greenColor];
    label.text = jsonDict[@"object_or_array"];
    [self.view addSubview:label];
});

Unless you are running into performance issues, there is no reason not to.