草庐IT

为什么人们总是对 Objective-C(即 iPhone)中的实例变量使用重新赋值?

codeneng 2023-03-28 原文

Why do people always use reassignment for instance variables in Objective-C (namely iPhone)?

我总是在 viewDidLoad 方法中看到示例代码,而不是说,例如

1
someInstanceVar = [[Classname alloc] init];

他们总是去

1
2
3
Classname *tempVar = [[Classname alloc] init];
someInstanceVar = tempVar;
[tempVar release];

这是为什么?不是完全一样,只是更长吗?

  • 我从未见过这样的构造,您在哪个示例代码中找到了它?能否请您更具体一点。 vartempVar 是成员变量吗?
  • 不仅你永远看不到它。这是不对的。您可能会看到类似的内容,中间线替换为 self.someInstanceVar = tempVar;


简短的回答:这种模式一直出现在 iPhone 代码中,因为它被认为是创建新对象并将其分配给成员变量的最佳方式,同时仍然尊重所有内存管理规则并调用适当的一面效果(如果有的话),同时也避免使用自动释放。

详情:

您的第二个示例将创建一个僵尸,因为 var 保留了一个指向已释放内存的指针。一个更可能的用例如下所示:

1
2
3
tempVar = [[Classname alloc] init];
self.propertyVar = tempVar;
[tempVar release];

假设 propertyVar 是声明为 copyretain 属性,此代码将新对象的所有权移交给类。

更新 1:以下代码是等效的,但在 iOS 上不推荐*,这可能是大多数 iPhone 程序改用第一种模式的原因。

1
self.propertyVar = [[[Classname alloc] init] autorelease];

* 在 iOS 上不鼓励自动释放,因为过度使用它会导致问题。确保您永远不会过度使用它的最简单方法是永远不要全部使用它,因此您会经常看到使用 alloc/initrelease 的 iOS 代码,即使 autorelease 是可以接受的。这是编码器偏好的问题。

更新 2:由于 Cocoa 在幕后自动执行的内存管理,这种模式起初看起来令人困惑。这一切的关键是用于设置成员变量的点表示法。为了帮助说明,请考虑以下两行代码是相同的:

1
2
self.propertyVar = value;
[self setPropertyVar:value];

当您使用点符号时,Cocoa 将调用指定成员变量的属性访问器。如果该属性已被定义为 copyretain 属性(这是该模式在不创建僵尸的情况下工作的唯一方法),那么会发生几件非常重要的事情:

  • 之前存储在 propertyVar 中的任何值都会被释放
  • 新值被保留或复制
  • 自动处理任何副作用(例如 KVC/KVO 通知)
    • 我的错,修复了代码示例。仍然不明白他们为什么这样做。
    • instanceVar = valuethis.instanceVar = value 之间有一个关键的区别。使用点符号告诉编译器调用属性访问器,它会在后台自动处理内存管理。
    • 好的,所以点符号调用属性访问器,没有点它只是设置实例变量?...我想念 java...
    • 你通常不使用 self.[a€|] 吗? @marty: self.instanceVar 访问 .h 文件中设置的属性。这也将 retain instanceVar 的新值。
    • @marty:是的,那是正确的 :) 起初这是一个棘手的概念,但如果它咬到你一次,你将永远不会忘记。
    • @e.James @marty 不,这并不完全正确。您需要整个 self. 部分才能实际访问该属性。
    • 哦,因为它使用了属性访问器,所以它被保留了。但我仍然不确定原来的问题。制作临时变量和直接初始化实例变量有什么区别?
    • @marty:我在答案中添加了更多细节,我认为这可能有助于解释。您可以像在第一个示例中那样直接分配给实例变量,但这会让您在以后遇到错误。一个潜在的问题是 instanceVar 中的旧值在分配新值之前没有释放。显然,如果 instanceVar 中没有值,这不是问题,但与其依赖该假设,更容易始终使用"分配、设置属性、释放"模式并轻松睡眠。
    • 您解释了分配给属性的不同之处,但不是为什么在将其分配给属性之前将其分配给临时 var 的原因 - 为什么不简单地 this.propertyVar =[[SomeClass alloc] init]?
    • 好问题。您必须使用中间变量,以便在完成后释放一些东西。如果你调用'self.propertyVar = [[Someclass alloc] init];',你可以很容易地泄漏新对象。例如,假设"propertyVar"是一个复制属性。然后属性访问器将尽职地复制该新对象,并且该副本将在您的"dealloc"方法中正确释放,但原始对象将被泄露。
    • 我的荣幸。我很高兴你提出了最后一点。我做了这么久,有时我忘记解释一些细节。
    • 你的意思是 self 而不是 this
    • @Chuck:啊。是的,我当然愿意。感谢您发现这一点。
    • 避免在 iPhone 上使用 autorelease 的建议可能是 Apple 网站上最容易被误解的建议。在这种情况下,例如,自动释放新创建的实例可能会产生最小的影响,因为它可能应该在当前运行循环事件结束后仍然存在。避免自动释放是过早优化的一种情况,因此部分是万恶之源。
    • @JeremyP:这是一个棘手的问题。即使在这个例子中,如果这个代码块只被调用了几次,自动释放也是完全无害的。但是,如果从循环中调用相同的代码块,则影响可能会非常严重。由于很难保证不会从循环中调用特定的代码块,因此最简单的安全方法是完全避免自动释放。
    • @JeremyP:我不会真正称其为优化问题。这更像是 goto 的使用:虽然使用它当然不会导致代码出现问题,但完全不使用它肯定更安全。
    • @e.James:如果,如果,如果...配置文件以找出问题并在您知道存在问题时进行优化。在任何情况下,将循环的每次迭代都package在一个自动释放池中可能比跳过循环以避免自动释放更好。
    • @JeremyP:我同意应该避免过早的优化,但我不认为 alloc/init, assign, release 模式是一种优化。对我来说,这更像是一个方便和安全的问题。如果你总是使用这种模式,你永远不必担心自动释放的潜在副作用。
    • @e.James:并非所有优化都与速度有关。内存占用也有优化。在大多数情况下,alloc/init 和 release 可能比 autorelease 更危险,因为内存泄漏的可能性更大。例如,-stringWithFormat: 为您提供了一个自动释放的对象,您可以在使用后忘记它,但 alloc/initWithFormat: 要求您分配一个临时变量并稍后释放该临时变量。代码更难看,如果你忘记了释放 - 内存泄漏。
    • @JeremyP:我熟悉不同类型的优化,我当然可以看到你对这个问题的看法:避免自动释放以降低临时内存使用量,这是一种内存占用优化。如果您的应用程序是设备上唯一运行的应用程序,并且您可以轻松地分析大多数用例,那么没有理由避免自动释放。
    • 我对此的看法是,由于 iPhone 和其他 iDevices 内存有限,而且您永远不知道您将拥有多少空间,因此最安全的选择是避免自动释放。 Apple 的开发工具很好地发现了您可能通过临时变量引入的任何内存泄漏。

    有关为什么人们总是对 Objective-C(即 iPhone)中的实例变量使用重新赋值?的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    4. 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

    5. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

      很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

    6. ruby - 在 Ruby 中使用匿名模块 - 2

      假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

    7. ruby - 其他文件中的 Rake 任务 - 2

      我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

    8. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

      作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

    9. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

      我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

    10. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    随机推荐