带有异步调用的空 UITableViewController

Posted

技术标签:

【中文标题】带有异步调用的空 UITableViewController【英文标题】:Empty UITableViewController with async calls 【发布时间】:2018-10-22 06:32:30 【问题描述】:

我正在使用 NSURLSession 和 JSON 序列化从我的网站获取内容。异步调用和获取 JSON 数据工作完美。我的问题是,在 TableviewController 中显示数据时,我放置了一个 NSLog 语句来查看是否有数据并且存在,但是 cell.textlable.text 永远不会更新。我猜问题是线程,但我无法弄清楚。你能帮忙吗?

@interface MainTableViewController : 
UITableViewController<LokalModelProtocol>


@property (strong,nonatomic) NSMutableArray* arr;

@end

@implementation MainTableViewController
 @synthesize arr;

- (void)viewDidLoad 
[super viewDidLoad];
arr = [[NSMutableArray alloc]init];
LokalModel *lokal = [[LokalModel alloc]init];
lokal.delegate=self;
[lokal downloadItems];



-(void)itemsDownloaded:(NSMutableArray *)items

arr=items;
//NSLog(@"%@", items);
[self.tableView reloadData];


#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
#warning Incomplete implementation, return the number of sections
return 1;


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section 
#warning Incomplete implementation, return the number of rows
// return 1;

return [arr count];



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"mainCell" forIndexPath:indexPath];

PostModel *post = [[PostModel alloc]init];
post =[arr objectAtIndex:indexPath.row];


NSLog(@"%@", post.postTitle); ////this outputs the correct strings///////

cell.textLabel.text =[NSString stringWithFormat:@"%@", post.postTitle];
cell.detailTextLabel.text = post.postTitle;///neither of these do//////

return cell;


@end





@protocol LokalModelProtocol <NSObject,NSURLSessionDelegate>

+(void)itemsDownloaded:(NSMutableArray*)items;

@end

@interface LokalModel : NSObject

-(void)downloadItems;
@property (strong, nonatomic) NSMutableData* thedata;
@property (strong, nonatomic) NSString* urlString;
@property (strong, nonatomic) NSURL* theUrl;
@property (strong,nonatomic) id<LokalModelProtocol>delegate;
+(void)parseJson:(NSData*)data;

@end





id<LokalModelProtocol>delegate;

@implementation LokalModel;
@synthesize thedata,urlString,theUrl,delegate;

-(void)downloadItems
NSURL *theUrl = nil;
static NSString* urlString = @"https://balalatet.com/wp-json/wp/v2/posts";
theUrl=[NSURL URLWithString:urlString];

NSURLSession *currentSession= [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionDataTask *task = [currentSession dataTaskWithURL:theUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) 
    if (error)
        [NSException raise:@"error" format:@"%@",error.localizedDescription];
        NSLog(@"error1");
    
    else
        NSLog(@"success");
        [LokalModel parseJson:data];

    


];
[task resume];



+(void)parseJson:(NSData*)data

NSArray *jsonResults = [[NSArray alloc]init];
NSError *jsonerror;
jsonResults =[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonerror];

if (jsonerror)
    [NSException raise:@"json error" format:@"%@",jsonerror.localizedDescription];

NSMutableArray *posts = [[NSMutableArray alloc] init];
NSMutableDictionary *jsonElenent =[NSMutableDictionary dictionary];

for (NSMutableDictionary *d in jsonResults)

    jsonElenent=d;
    PostModel *thePost=[[PostModel alloc]init];
    thePost.postId= jsonElenent[@"id"];
    thePost.postDate= jsonElenent[@"date"];
    thePost.postDategmt= jsonElenent[@"date_gmt"];
    thePost.postGuid= jsonElenent[@"guid"];
    thePost.postSlug= jsonElenent[@"slug"];
    thePost.postStatus= jsonElenent[@"status"];
    thePost.postSticky= jsonElenent[@"sticky"];
    thePost.postPingStatus= jsonElenent[@"ping_status"];
    thePost.postType= jsonElenent[@"type"];
    thePost.postCommentStatus= jsonElenent[@"comment_status"];
    thePost.postTags= jsonElenent[@"tags"];
    thePost.postTitle= jsonElenent[@"title"];
    thePost.postTemplate= jsonElenent[@"template"];
    thePost.postLink= jsonElenent[@"link"];
    thePost.postMeta= jsonElenent[@"meta"];
    thePost.postModified= jsonElenent[@"modified"];
    thePost.postModifiedgmt= jsonElenent[@"modified_gmt"];
    thePost.postFeaturedMedia= jsonElenent[@"featured_media"];
    thePost.postFormat= jsonElenent[@"format"];
    thePost.postLinks= jsonElenent[@"links"];
    thePost.postAuthor= jsonElenent[@"author"];
    thePost.postContent= jsonElenent[@"content"];
    thePost.postCategory= jsonElenent[@"category"];
    thePost.postExcerpt= jsonElenent[@"excerpt"];
    NSLog(@"%@", thePost.postTitle);
    [posts addObject:thePost];

dispatch_async(dispatch_get_main_queue(), ^
    [delegate itemsDownloaded:posts];
);

@end

更新

作为调试信息的一部分,我的道歉不正确。 nslog 输出不是来自 cellForRowAtIndexPath 方法。实际上 arr 数组仍然是空的,因为 (void)itemsDownloaded:(NSMutableArray *)items 永远不会被调用。我确定我正确设置了协议。为什么 MainTableViewControllerClass 无法获取数据有什么想法吗?

更新

所以我意识到我忘了删除线

id<LokalModelProtocol>delegate;

我把它放在 LokalModel 中的 @implementation 之前。但现在这样做会导致错误“无法识别的选择器发送到实例”在

[delegate itemsDownloaded:posts];

我也试过了

[self.delegate itemsDownloaded:posts];

但它会引发相同的异常。

已解决

我的协议方法必须是实例方法,并且我将其设置为类方法。

【问题讨论】:

你能检查cell.textLabelcell.detailTextLabel吗?,打印它们,这也应该给它们的框架。不相关,但PostModel *post = [[PostModel alloc]init]; post =[arr objectAtIndex:indexPath.row];应该是PostModel *post = [arr objectAtIndex:indexPath.row];,因为如果您在之后替换它,则不需要分配/初始化一个对象。 感谢您的提示,但在这种情况下,问题出在其他地方。 NSLog 输出浪费了 cellForRowAtIndexPath 方法。事实上,itemsDownloaded:(NSMutableArray *)items 函数从未被调用过,所以我一直在处理一个空数组。 那是不同的信息。 Lokal 需要成为一个强大的属性。我想到了,但你的问题和代码暗示 cellFoRow 被调用(对 NSLog 的评论)。 是的,我同意,我不确定 *** 在这种情况下想要什么。在我进行更多调查后,我应该稍后创建一个新线程吗?我让 lokal 成为一个强大的属性,但仍然有同样的问题 【参考方案1】:

在返回您的单元格添加之前,请尝试在 cellForRowIndexPath 中添加此代码

[单元格布局如果需要];

【讨论】:

这对我不起作用。一点旁注。如果我更改 numberOfRowsInSection 以返回一个常量,应用程序会由于空指针而崩溃。但将其设置为 [arr count] 会给我一个空的 tableview 你检查过arr计数吗?不是零吗?在 itemsDownloaded 中的 copt 项目之后,您是否还检查了您的数组? 好的,所以我刚刚检查了一下,计数为零,然后我在问题定义中发现了一个错误。我在控制台中看到的输出不是来自 cellForRowAtIndexPath。所以这意味着到目前为止我不知道数据是否正确传递。我可能只是在处理空数组。谢谢,我会尽快更新【参考方案2】:

我相信您必须在使用 dequeueReusableCellWithIdentifier:forIndexPath: 之前添加 registerNib:forCellReuseIdentifier:registerClass:forCellReuseIdentifier:(例如在 viewDidLoad 中)

来自文档:https://developer.apple.com/documentation/uikit/uitableview/1614878-dequeuereusablecellwithidentifie?language=objc

重要 在调用此方法之前,您必须使用 registerNib:forCellReuseIdentifier: 或 registerClass:forCellReuseIdentifier: 方法注册类或 nib 文件。

【讨论】:

我不认为是这种情况,因为如果我创建一个虚拟数字数组,例如,数据会正确显示。但在我的代码中,由于我异步获取数据,我猜测问题是 numberOfRows 和 cellForRowAtIndexPath 方法在我什至在数组中获取数据之前就完成了。但我不知道如何解决它。

以上是关于带有异步调用的空 UITableViewController的主要内容,如果未能解决你的问题,请参考以下文章

记录一些遇见的bug——记录一个使用多线程异步调用openfeign时子线程丢失request请求头导致的空指针异常错误

将 2 个对象添加到带有部分的空商店时,UITableView 崩溃(NSRangeException)

异步任务的空指针异常

RestEasy 异步请求中的空指针异常

RestEasy 异步请求中的空指针异常

异步函数调用,带有检查进度的方法