jjzjj

objective-c - 如何在NSTimer循环中刷新TableView Cell数据

coder 2024-01-22 原文

免责声明:我是iOS的新用户,请随时告诉我我的整个方法是错误的。我只要求您解释原因-我的目标是学习(当然,也要解决这个问题)。

...

大家好,我是iOS编程的新手,在尝试解决了几个小时的问题后,我的无知和可可缺乏的经验使我得到了最大的收获。希望我能在这里找到一些帮助。

我有一个简单的TableView应用程序,正在将其用作正在开发的大型应用程序的沙箱。我现在要实现的功能是TableView单元格(即cell.detailTextLabel.text)中字幕文本的伪实时更新。

为了使您更好地了解目标,我正在努力做到这一点,以便用户可以点击TableView单元格,并且“秒表”样式计时器开始在字幕文本中进行计数。

但是我无法弄清楚如何更新cell.detailTextLabel.text。A)从我的计时器方法内部(它说未声明tableView)和B)以一种反复刷新单元格的方式让用户看到一个“ Activity ”计时器。

因此,这是一些代码(所有这些现在都在RootViewController中。最终,我想为诸如定时器之类的功能创建单独的类,但我只想让事情先开始工作)。

我还应该补充一点,在此类中,我现在有很多全局变量(“接口?”)。它使实验变得更容易,但是如果这可能导致某些问题,请告诉我。我仍在学习如何来回传递对象/变量/等。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    self.runTimerFunctions;
}

然后是计时器函数(这些是接口变量):
- (void) runTimerFunctions {

    start = CFAbsoluteTimeGetCurrent();
    timerRunning = YES;
    timer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target:self selector:@selector(targetMethod:) userInfo:nil repeats:YES];  
}

数学和计算本身。产生格式为MM:SS.hh的单个字符串“liveTimer”,该字符串可以准确地向上“秒表”:
- (void)targetMethod: (NSTimer *)theTimer {

    // NOTE TO SELF -- right now I've got hours, minutes, seconds, etc as global variables. But that may be unnecessary.

    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    CFAbsoluteTime timeDelta = end-start;
    int integerSecs = timeDelta;
    int integerCentiSecs = timeDelta * 100;

    hundredths = integerCentiSecs % 100;
    NSString *hundredthsString = [NSString stringWithFormat:@"%02i", hundredths];

    seconds = integerSecs % 60;
    NSString *secondsString = [NSString stringWithFormat:@"%02i", seconds];

    minutes = (integerSecs / 60) % 60;
    NSString *minutesString = [NSString stringWithFormat:@"%02i", minutes];

    // Now combine these value strings into the single liveTimer string

    liveTimer = [NSString stringWithFormat:@"%@:%@.%@", minutesString, secondsString, hundredthsString];

}

好的,所以liveTimer字符串是一个接口变量,因此我可以对其进行修改并在整个过程中进行访问(这可能不是这样做的方法,我又是一个菜鸟,因此请在需要时进行更正)。

在最初的UITableViewCell创建位中,我将liveTimer用作详细信息文本值,如下所示:
cell.textLabel.text = @"Name";
NSLog(@"liveTimer value is %@", liveTimer);
cell.detailTextLabel.text = liveTimer;

因此,从本质上讲,我现在所拥有的方式是,当用户按下列表项时,计时器开始在后台进行计数(我已通过NSLog确认其工作正常-如果我在targetMethod末尾提取liveTimer则正确格式化并向上计数。

但是,如何通过单元格文本的伪实时更新来反映这一点?

当我将程序放在任何地方都可以使[tableView reloadData]不断崩溃的程序(没有错误代码),但是我不知道如何从targetMethod中访问它(当我添加[tableView reloadData]时,它告诉我tableView未声明)。

此外,即使我尝试将其实验性地放入例如didSelectRowAtIndexPath中,[tableView reloadData]仍然使程序崩溃(没有错误消息)。例如
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    self.runTimerFunctions;

    [tableView reloadData];

从本质上讲,我敢肯定有一种简单的方法可以完成我想做的事情,但是对于该语言我还是太陌生,不知道如何去做。如果通过在计时器循环期间尝试调用reloadData重载系统,我不会感到惊讶。但是话又说回来,也许还有别的东西。

任何建议将不胜感激。我试图在这里学习这种语言,尽管示例代码非常有帮助,但是我同样对“学习如何钓鱼”感兴趣,因此我非常感谢对为什么我做错了以及更好的方法可能是,等等。

预先感谢您的所有帮助;我期待参与论坛。

最佳答案

首先,一个小虫子...
liveTimer = [NSString stringWithFormat:@"%@:%@.%@", minutesString, secondsString, hundredthsString];
这可能是为什么在调用reload tableView时崩溃的原因。

您实际上想要:

要么:

self.liveTimer = [NSString stringWithFormat:@"%@:%@.%@", minutesString, secondsString, hundredthsString];

(如果已将liveTimer配置为属性,而不仅仅是成员变量)

要么
[liveTimer release];
liveTimer = [[NSString stringWithFormat:@"%@:%@.%@", minutesString, secondsString, hundredthsString] retain];

要么
[liveTimer release];
liveTimer = [[NSString alloc] initWithFormat:@"%@:%@.%@", minutesString, secondsString, hundredthsString];
stringWithFormat:...返回一个自动释放的字符串,这意味着它将在运行循环的当前旋转结束时被释放。这将导致liveTimer基本上在函数完成后指向未定义的内存。使用self.liveTimer而不是liveTimer会导致调用合成的setter方法,而不是直接设置变量,这会导致对象在设置字符串时保留其字符串(假设您已经设置了live timer属性以保留该字符串,如下所示) :@property (nonatomic, retain) NSString *liveTimer;上面列出的其他两个选择都释放了旧版本的字符串,然后保留了新的字符串并进行设置,然后直接设置了变量。第二个是愚蠢的(它绕行创建字符串,自动释放它,然后再次保留它。)

现在,关于您的问题。

- (void)targetMethod: (NSTimer *)theTimer的末尾,您将需要实际更新当前存在的表单元格。假设tableCell位于第0节的第0行:
-(void)targetMethod: (NSTimer *)theTimer {    

    // Everything you already had here, then...

    NSIndexPath *cellIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    UITableViewCell *currentCell = [tableView cellForRowAtIndexPath:cellIndexPath];
    // Returns the cell that's currently in the tableview at this location

    currentCell.detailTextLabel.text = self.liveTimer;
}

这样做应该...如果当前单元格不存在(因为它已滚动到屏幕外),则cellForRowAtIndexPath:将返回nil,而currentCell.detailTextLabel.text = self.liveTimer;将不执行任何操作(即,没有错误,实际上什么也没有)。

编辑:

我只是注意到tableView不是您的对象成员变量之一...

您是否有可以到达的UITableViewController?如果是这样,只需在当前使用tableView的任何地方使用tableViewControllerVariableName.tableView。您的RootViewController(所有这些代码都在其中)是UITableViewController吗?如果是这样,请在您当前调用tableView的任何地方调用self.tableView。如果没有,则将tableView / tableViewController分配到哪里?发布您的头文件可能有助于清除问题...

编辑2:

liveTimer与self.liveTimer的问题在于对象的保留计数。
liveTimer = [NSString stringWithFormat:@"%@:%@.%@", minutesString, secondsString, hundredthsString];

确实看起来像它-将liveTimer变量设置为新字符串。鉴于
self.liveTimer = [NSString stringWithFormat:@"%@:%@.%@", minutesString, secondsString, hundredthsString];

有点神奇。使用self.前缀(或就此而言,anyObject.)告诉编译器“我在这里真正想做的就是为liveTimer属性调用accessor方法”。编译器实质上将上述行替换为:
[self setLiveTimer:[NSString stringWithFormat:@"%@:%@.%@", minutesString, secondsString, hundredthsString]];

由于您大概在类文件中的某个位置有@synthesize liveTimer;,因此编译器还会自动为您创建-(void)setLiveTimer:(NSTimer *)timer-(NSTimer *)liveTimer方法。他们看起来像这样:
-(void)setLiveTimer:(NSTimer *)newLiveTimer
{
    [liveTimer release];  // This is the old value.  It may be nil, which is fine.  Calling methods on nil objects in objective-c is just be a noop, not a crash.
    liveTimer = [newLiveTimer retain];
}

-(NSTimer *)liveTimer
{
    return liveTimer;
}

(这有点过分简化,但这很重要。)

因此,当您调用self.liveTimer时,setLiveTimer:方法与对象一起调用,旧对象(如果有的话)被释放,而新对象被保留。
[NSString stringWithFormat:...]返回一个自动释放的对象。当控件从程序的代码返回到苹果提供的运行循环代码后,自动释放的对象将被释放。它们或多或少是一种便利,因此我们不必在这里和那里一两次使用所有的小对象release。 (例如,想象一下,如果必须释放使用@“”语法创建的每个字符串,那将是多么繁琐的工作。)

我们可以说stringWithFormat:返回一个自动释放的对象,因为按照惯例,名称不以alloc开头的方法或copy总是返回自动释放的对象。据说像这样的方法“贩卖”了一个对象。我们可以在不久的将来使用这些对象,但是我们不能“拥有”它(即,在将控制权交还给系统之后,我们不能指望它在那里。)如果我们想获得出售对象的所有权,我们必须在其上调用[object retain],然后它将一直存在,直到我们显式调用[object release][object autorelease]为止;如果在不通过将变量更改为其他名称而丢失对它的引用之前,不对它调用releaseautorelease,我们会泄漏它。

[[NSString alloc] initWithFormat:对比。此方法“创建”一个对象。我们拥有它。同样,它将一直存在,直到我们显式调用[object release]为止。

因此,您正在将成员变量liveTimer设置为新字符串,但是该字符串已自动释放,一旦- (void)targetMethod: (NSTimer *)theTimer返回,控件便立即返回到系统的运行循环代码,并且该字符串已被释放。这意味着liveTimer指向内存中的某个随机点,该点在某一时刻包含了您的字符串对象,但现在包含了其他东西-未定义的东西。当您在内存中的那个随机位置调用方法时,它崩溃了。

(为了进一步混淆,[NSTimer scheduledTimerWithTimeInterval:target:selector:]是一个特例。计时器会一直保留直到触发为止,这就是代码的那部分起作用的原因。没人真正知道NSTimer为什么会得到异常;这真是令人困惑,它被NExT遗留下来了。天-90年代初编写的代码,对某人来说似乎是个好主意。)

因此,您是否想在所有地方使用self.liveTimerself.timerself.whatever?或多或少。您通常希望保留新对象并释放旧对象。唯一的例外之一是,如果您使用[[Object alloc] init]“创建”一个对象,则该对象已经具有保留计数1,在这种情况下,使用self.语法将第二次保留该对象,即可能不是您想要的。 (尽管,如果您之前设置了self.liveTimer =,然后在代码中稍后使用liveTimer =,则第一个字符串将被泄漏,因为从未发布过该字符串。决定执行该方法的方式并不总是那么琐碎。)

希望能使事情变得简单。我认为,objective-c中的点缀符号是语言中最不直观的部分。确保阅读有关内存管理的苹果文档:

http://developer.apple.com/library/ios/#documentation/general/conceptual/DevPedia-CocoaCore/MemoryManagement.html

关于objective-c - 如何在NSTimer循环中刷新TableView Cell数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5427196/

有关objective-c - 如何在NSTimer循环中刷新TableView Cell数据的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  3. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  4. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  5. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  6. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  7. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  8. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  9. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  10. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

随机推荐