草庐IT

objective-c - 将 iVar 放在 "modern"Objective-C 的何处?

coder 2023-04-25 原文

Ray Wenderlich 所著的“iOS6 by Tutorials”一书中有一个关于编写更多“现代”Objective-C 代码的非常好的章节。在一节中,这些书描述了如何将 iVar 从类的头文件移动到实现文件中。
由于所有 iVar 都应该是私有(private)的,这似乎是正确的做法。

但到目前为止,我发现了 3 种方法。每个人都在做不同的事情。

1.) 将 iVars 放在 @implementantion 下的花括号块中(这就是本书中的做法)。

2.) 将 iVars 放在 @implementantion 下,不带花括号块

3.) 将 iVars 放在 @implementantion 之上的私有(private)接口(interface)中(类扩展)

所有这些解决方案似乎都运行良好,到目前为止我还没有注意到我的应用程序的行为有任何不同。
我想没有“正确”的方法可以做到,但我需要编写一些教程,并且我只想为我的代码选择一种方法。

我应该走哪条路?

编辑:我在这里只谈论 iVars。不是属性。只有对象本身需要的附加变量,不应暴露给外部。

代码示例

1)

#import "Person.h"

@implementation Person
{
    int age;
    NSString *name;
}

- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

2)
#import "Person.h"

@implementation Person

int age;
NSString *name;


- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

3)
#import "Person.h"

@interface Person()
{
    int age;
    NSString *name;
}
@end

@implementation Person

- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

最佳答案

将实例变量放入 @implementation 的能力块,或在类扩展中,是“现代 Objective-C 运行时”的一个特性,每个版本的 iOS 和 64 位 Mac OS X 程序都使用它。

如果您想编写 32 位 Mac OS X 应用程序,您必须将您的实例变量放在 @interface 中。声明。不过,您可能不需要支持 32 位版本的应用程序。自 5 年前发布的 10.5 (Leopard) 版本以来,OS X 一直支持 64 位应用程序。

因此,让我们假设您只编写将使用现代运行时的应用程序。你应该把你的ivars放在哪里?

选项 0:在 @interface (不要这样做)

首先,让我们回顾一下为什么我们不想将实例变量放在 @interface 中。声明。

  • 将实例变量放入 @interface向类的用户公开实现的细节。这可能会导致这些用户(甚至在使用您自己的类时您自己!)依赖他们不应该依赖的实现细节。 (这与我们是否声明 ivars @private 无关。)
  • 将实例变量放入 @interface使编译需要更长的时间,因为每当我们添加、更改或删除 ivar 声明时,我们都必须重新编译每个 .m导入接口(interface)的文件。

  • 所以我们不想把实例变量放在 @interface 中.我们应该把它们放在哪里?

    选项 2:在 @implementation没有大括号(不要这样做)

    接下来,让我们讨论您的选项 2,“将 iVars 放在 @implementantion 下而不使用花括号块”。这是不是 声明实例变量!你在谈论这个:
    @implementation Person
    
    int age;
    NSString *name;
    
    ...
    

    该代码定义了两个全局变量。它不声明任何实例变量。

    可以在您的 .m 中定义全局变量文件,即使在您的 @implementation 中,如果您需要全局变量 - 例如,因为您希望所有实例共享某些状态,例如缓存。但是你不能使用这个选项来声明 ivars,因为它没有声明 ivars。 (此外,您的实现私有(private)的全局变量通常应声明为 static 以避免污染全局命名空间和链接时错误的风险。)

    这就留下了您的选项 1 和 3。

    选项 1:在 @implementation带牙套(Do It)

    通常我们想使用选项 1:把它们放在你的主 @implementation 中。块,在大括号中,像这样:
    @implementation Person {
        int age;
        NSString *name;
    }
    

    我们把它们放在这里是因为它保持它们的存在是私有(private)的,防止我之前描述的问题,并且因为通常没有理由将它们放在类扩展中。

    那么我们什么时候要使用您的选项 3,将它们放在类扩展中?

    选项 3:在类扩展中(仅在必要时执行)

    几乎没有理由将它们与类的 @implementation 放在同一个文件中的类扩展名中。 .我们不妨把它们放在 @implementation 中在那种情况下。

    但有时我们可能会编写一个足够大的类,以至于我们想将其源代码分成多个文件。我们可以使用类别来做到这一点。例如,如果我们正在实现 UICollectionView (一个相当大的类),我们可能会决定将管理可重用 View (单元格和补充 View )队列的代码放在一个单独的源文件中。我们可以通过将这些消息分成一个类别来做到这一点:
    // UICollectionView.h
    
    @interface UICollectionView : UIScrollView
    
    - (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
    @property (nonatomic, retain) UICollectionView *collectionViewLayout;
    // etc.
    
    @end
    
    @interface UICollectionView (ReusableViews)
    
    - (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
    - (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;
    
    - (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier;
    - (void)registerNib:(UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier;
    
    - (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
    - (id)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
    
    @end
    

    好的,现在我们可以实现主要的 UICollectionView UICollectionView.m 中的方法我们可以在 UICollectionView+ReusableViews.m 中实现管理可重用 View 的方法,这使我们的源代码更易于管理。

    但是我们可重用的 View 管理代码需要一些实例变量。这些变量必须暴露给主类 @implementationUICollectionView.m ,所以编译器会在 .o 中发出它们文件。我们还需要将这些实例变量暴露给 UICollectionView+ReusableViews.m 中的代码。 ,所以这些方法可以使用 ivars。

    这是我们需要类扩展的地方。我们可以将可重用 View 管理变量放在私有(private)头文件中的类扩展中:
    // UICollectionView_ReusableViewsSupport.h
    
    @interface UICollectionView () {
        NSMutableDictionary *registeredCellSources;
        NSMutableDictionary *spareCellsByIdentifier;
    
        NSMutableDictionary *registeredSupplementaryViewSources;
        NSMutableDictionary *spareSupplementaryViewsByIdentifier;
    }
    
    - (void)initReusableViewSupport;
    
    @end
    

    我们不会将此头文件发送给我们库的用户。我们只需将其导入 UICollectionView.m并在 UICollectionView+ReusableViews.m ,以便所有需要看到这些 ivars 的东西都能看到它们。我们还抛出了一个我们想要主 init 的方法。调用以初始化可重用 View 管理代码的方法。我们将从 -[UICollectionView initWithFrame:collectionViewLayout:] 调用该方法在 UICollectionView.m ,我们将在 UICollectionView+ReusableViews.m 中实现它.

    关于objective-c - 将 iVar 放在 "modern"Objective-C 的何处?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13566862/

    有关objective-c - 将 iVar 放在 "modern"Objective-C 的何处?的更多相关文章

    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-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

      我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

    3. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

      我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

    4. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

      为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

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

    6. 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中的所有其他对象

    7. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

      我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

    8. ruby-on-rails - 迷你测试错误 : "NameError: uninitialized constant" - 2

      我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test

    9. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

      我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

    10. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

      我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

    随机推荐