V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
meta
V2EX  ›  C

小弟初学objective-c,对于extension的作用一直无法理解,请各位前辈指点一二。

  •  
  •   meta · 2013-08-31 09:45:05 +08:00 · 2177 次点击
    这是一个创建于 4100 天前的主题,其中的信息可能已经有所发展或是发生改变。
    看到很多书上都说,扩展可以用来实现类的私有实例变量和方法,那么把方法从.h文件中去掉,使其不被引用到,不就实现私有方法了么,为什么要写成扩展的方式呢。
    又看到有说如果可以在.h中将属性置为readonly而在.m的扩展中再设置为readwrite,这样就能保证属性对外部是只读的而对内部是读写的,但是只要在.m中写个@synthesize x = _x;不也能达到这样的效果么。

    所以对使用扩展到底有什么用处一直搞不明白,请各位赐教。
    28 条回复    1970-01-01 08:00:00 +08:00
    xhacker
        1
    xhacker  
       2013-08-31 10:07:38 +08:00 via iPad   ❤️ 1
    从 .h 里去掉就要放在 extension 里啊。
    property 就是自动生成 synthesize 的,不用手动写。
    meta
        2
    meta  
    OP
       2013-08-31 10:19:46 +08:00
    @xhacker extention不是要写成:@interface XXXClass()吗,我的意思是说不这样写又有什么区别呢。
    property那个不这样写的话怎么实现外部只读,内部可读写的功能呢?
    xhacker
        3
    xhacker  
       2013-08-31 10:22:56 +08:00
    不写成 extension 你想怎么写?
    property 就按你说的那么写就对,我是说不应当手动写 synthesize。
    meta
        4
    meta  
    OP
       2013-08-31 10:29:26 +08:00
    @xhacker 比如:

    //Base.h
    @interface Base : NSObject
    @property int x;

    @end


    //Base.m
    @implementation Base

    @synthesize x = _x;

    -(int) fetchX{
    return _x;
    }
    @end

    这样不是私有方法么,和扩展的区别是什么呢。
    xhacker
        5
    xhacker  
       2013-08-31 10:32:56 +08:00
    @meta: 这样不是私有,外面可以访问到吧。
    另外 getter 应该叫 x 而不是什么 fetchX。
    meta
        6
    meta  
    OP
       2013-08-31 10:36:14 +08:00
    @xhacker .h文件里面没有,外面怎么访问呢。
    写成fetchX就是为了和getX区别,好显示效果,getX就是存取方法了,不好测试。
    walkingway
        7
    walkingway  
       2013-08-31 11:04:44 +08:00   ❤️ 1
    @property 和 @synthesize的作用就是生成一对getter 和 setter方法,你在.h里声明了一个@property 其实就声明一个实例变量的getter和setter方法,新的LLVM下已经不用手动再去.m里写@synthesize了(当然如果你手动重载了getter和setter,是要写的)

    你虽然写了个fetchX,但你在.h里声明了个@property,外面还是能通过getter方法访问的。
    xhacker
        8
    xhacker  
       2013-08-31 11:10:29 +08:00
    @meta: 简而言之很简单:要 public 就把 property 写 .h 里,要 private 就写 .m 里,要对外只读就在 .h 里写 readonly、.m 里写 readwrite。
    meta
        9
    meta  
    OP
       2013-08-31 12:22:40 +08:00
    看来楼上两位没有明白我的问题,自动生成的setter和getter显然是public的,这个我清楚。我疑惑的是extension该用在什么地方的问题,所以请不要纠结那个property。

    可能是我举例不太恰当,有些误导,现在我来重新写一下。

    问题一、方法的作用域的问题

    //-----1-----
    //Base.h
    @interface Base : NSObject{
    int x;
    }

    @end


    //Base.m

    @interface Base()
    -(int) fetchX;
    @end

    @implementation Base

    -(int) fetchX{
    return x;
    }
    @end


    //-----2-----
    //Base.h
    @interface Base : NSObject{
    int x;
    }

    @end


    //Base.m
    @implementation Base

    -(int) fetchX{
    return x;
    }
    @end


    写法1是我在书上看到的是用扩展的一个场景,说是可以讲fetchX置为私有,但我认为写法2也可以,请问以上两种写法的区别在哪里。

    问题二、改变属性存取权限的问题
    //-----1-----

    //Base.h
    @interface Base : NSObject
    @property (readonly)int x;

    @end


    //Base.m

    @interface Base()
    @property (readwrite)int x;
    @end

    @implementation Base
    @end

    //-----2-----
    //Base.h
    @interface Base : NSObject
    @property (readonly)int x;

    @end


    //Base.m

    @implementation Base
    @synthesize int x=_x;
    @end


    写法1是我在网上找到的另一个应用扩展的场景,可以将readonly的x变成内部readwrite,但我认为写法2直接内部访问_x也可以达到同样的效果,请问这两种写法有什么不同吗。

    由此,我一直不理解到底什么场景应该使用扩展。
    ldehai
        10
    ldehai  
       2013-08-31 12:29:26 +08:00
    顾名思义,扩展就是扩展已有类的功能。这个已有的类一般来说都不是自己写的,要么是系统基础类,要么是第三方的类,这个时候想要增加这个类的功能就用扩展。

    你所说的“扩展可以用来实现类的私有实例变量和方法“,只是说用扩展可以这么实现,不是说用其他方式不能实现。我觉得这不是扩展设计的初衷。
    ldehai
        11
    ldehai  
       2013-08-31 12:46:33 +08:00
    给个例子你参考一下,一个UIColor类的扩展:https://github.com/ldehai/UIColor-Categories

    使用的时候给一个按钮设置背景颜色
    btn.backgroundColor = [UIColor colorWithHexString:@"#E0E4CC"];
    ichord
        12
    ichord  
       2013-08-31 12:58:10 +08:00
    首先, 我也是初学者, 只是看到你的问题也顺便查阅资料学习学习. 纯讨论哈.

    @ldehai 你说的是 `category` 吧. `extension` 才是用于扩展自己写的类的啊.

    @meta 我觉得你没有考虑继承的问题, 面向对象很基础的问题吧. 私有的东西是不被继承的吧.
    * 类变量默认是 `protected` 的.
    * `extension` 是不被子类继承的(除非你引入它的 .h 文件)

    所以

    And...
    meta
        13
    meta  
    OP
       2013-08-31 12:59:12 +08:00
    @ldehai 你这个例子是category啊,category我能理解,我不理解的是extension,extension和原来的类是不能分开实现的,不能像这个例子中这么做。
    ichord
        14
    ichord  
       2013-08-31 12:59:36 +08:00   ❤️ 1
    = =.... 我还没编辑完......
    anyway. 反正大概意思算是表达了.
    meta
        15
    meta  
    OP
       2013-08-31 13:01:20 +08:00
    @ichord 写在.h中的变量才是protected的吧,写在.m中的变量不应该是private的吗?
    ichord
        16
    ichord  
       2013-08-31 13:01:58 +08:00
    再补一条, 我在 <Programming iOS> 的 objective-c 章节里看到. 用 synthesize 自动生成的 instance variable 也默认是 private 的.....
    meta
        17
    meta  
    OP
       2013-08-31 13:03:43 +08:00
    @ichord 是的,所以说加上extension有什么作用呢。
    ichord
        18
    ichord  
       2013-08-31 13:03:43 +08:00
    @meta 我也搞不清楚. 先吃饭去. 待会回来写个例子一一验证好了.
    ultragtx
        19
    ultragtx  
       2013-08-31 13:55:08 +08:00
    看文档去 文档里都写了
    chchwy
        20
    chchwy  
       2013-08-31 16:57:59 +08:00   ❤️ 2
    首先你要明白一件事,最近兩三年Objective-C的語言演進很快,extension是舊時代留下來的產物。

    Xcode 4.2/LLVM 3.0 之後Objc語言引進了非常多新特性,語法也有相當變化。

    詳細可查看這張表:
    https://developer.apple.com/library/ios/releasenotes/ObjectiveC/ObjCAvailabilityIndex/

    Xcode 4.2 是 2011年10月發佈的,我猜你的書的出版日期可能在這之前。

    1. 以前只把方法和變量從.h移走,是沒辦法通過編譯的,私有方法一定要放在extension裡。
    2. 現在私有變量、私有方法都直接寫在@implementation block裡就好了,但是這都是在Xcode 4.2之後才有的功能,以前不能這樣寫的。
    3. 現在extension已經成了雞肋般的存在。唯一的用途就是有個類似.h的方法列表,閱讀上比較容易。
    ichord
        21
    ichord  
       2013-08-31 17:05:07 +08:00
    @meta 在实现的效果上我也完全找不到有什么区别了. 我也求解答!
    书上和网上的例子能看出的却别也就是用法概念之类的问题..

    https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#//apple_ref/doc/uid/TP40011210-CH6-SW6

    看这个官方的例子, 实际实现效果的区别也就是只有类内部可以使用 `[self setProperty:xxx]` 或者 `self.property = xxx` 这样的用法而已.

    ----

    * 在 @interface 声明的 instance variable 是 protected 的.
    * 在 @implement 声明的 instance variable 是 "private" 的.
    * @synthesize 自动声明的 instance variable 也是 "private" 的.
    * objective-c 里面的类方法只有两种:
    ** 在 @interface 声明的. "public"
    ** 没在 @interface 声明的. "private". (其实是没有这个概念的是吧?!)

    所以要声明 instance variable 或者 method 为私有的(private) 的话, 根本没必要在 class extension 的声明.

    ----

    我在 <Learning Cocoa with Objective-c> 的 `Class Extensions` 章节看到这两句话:

    >
    there are two reasons for extending a class:
    1. You want to add extra behavior and logic to an existing class
    2. You want to break up one of your own classes into separate components.


    我现在能想到的就是

    1. 配合 `category` 将一个大类进行各种拆分组合, 类似于 `Mixin` 的用法.
    因为 `category` 可以创建方法但不能创建类变量, 而另一个则可以创建类变量但不能创建方法. 而且方法声明貌似没什么用处...

    2. 将一个大类以不同的 @interface 组合暴露出去, 按适用需要 `import` (这个貌似也很弱的样子... = =)

    = =. 今天就先到这好了... 以后遇到再说...
    ichord
        22
    ichord  
       2013-08-31 17:07:13 +08:00
    @chchwy cool.
    xuzhe
        23
    xuzhe  
       2013-08-31 19:12:43 +08:00   ❤️ 1
    首先,

    _x = a;



    self.x = a;

    的区别你理解了吗?

    这个能理解的话,你的问题应该自己也能想明白了。
    walkingway
        24
    walkingway  
       2013-08-31 19:58:20 +08:00
    你举的两个例子:
    第一个 1和2 没有什么区别,只要不在.h里的方法默认都是私有方法

    第二个 你真正理解为什么要用 @synthesize x = _x 而不是 @synthesize x么?
    这么做的目的是为了方便区分属性x和实例变量_x而已,仅此而已。
    你在.h里声明x了只读,在.m的扩展里不改写为读写的话,是无法用self.x=...来赋值的。当然你说你直接用_x=...来赋值可以,但是,这么做也就跳过了getter和setter方法,当然不受readonly的限制。
    如果你getter和setter方法里做点特别的事情,你用_x就没法和self.x达到一样的效果了。

    不知道说明白没
    walkingway
        25
    walkingway  
       2013-08-31 20:07:03 +08:00
    简单说就是 self.x 和 self.x = ... 调用的是@property 生成的getter和setter方法,
    @synthesize x = _x 生成一个实例变量,为了不和属性x混淆改名为_x,以后你写_x就是直接访问的实例变量,不会调用getter和setter方法。你getter和setter方法里做的一些额外事情,也无法实现。
    xsown
        26
    xsown  
       2013-09-01 01:15:12 +08:00   ❤️ 1
    1) 用 extension 的方式在 .m 文件里把一个 property 从 readonly 改为 readwrite,改变的是这个 property 本身,这时候你就可以写 self.someProperty = xxx 而不会引起编译器报错。

    2) 如果你把这个 property 给手动 synthesize 到某个 ivar 上,然后修改 ivar 的值,表面上看确实与 readwrite 的效果等同。但是 property 上面的修饰词可能会造成一些区别,比如 atomic,那么当你写 self.someProperty = xxx 时,编译器实现的代码会是类似 someLockFunction(); _someProperty = xxx; someUnlockFunction(); 这样的形式,确保不同的线程同时访问这个 property 时不会出错。

    3) property 作为一种封装,getter 和 setter 有很大的自由度可以实现复杂的行为。而对 ivar 的读写仅仅是取值/赋值。所以 readwrite 化的 property 更加灵活。
    meta
        27
    meta  
    OP
       2013-09-01 09:42:07 +08:00
    非常感谢楼上各位,@chchwy 完全解决了我的疑惑,@walkingway 和@xsown 的解答也很有价值。我不是不了解属性和实例变量的区别,我疑惑的是使用场景,因为私有的属性和公有的属性使用场景是完全不一样,我不明白在什么场景需要用到私有的属性而不使用变量,固然可以简单的用self.x访问到自定义的getter和setter方法,但是这样比直接使用setX也简单不到哪里去。@xsowm说的线程安全的问题很有道理,但我认为既然是私有变量,不需像对外的接口一样需要形式上的一致,那么处理方法多种多样,没看出来一定要使用extension的理由。所以@chchwy 兄的介绍就让我完全明白了。

    这次真是获益匪浅,非常感谢大家。
    xuzhe
        28
    xuzhe  
       2013-09-01 10:11:02 +08:00
    很明显 chchwy 同学也没完全搞明白,他的第3点总结是不对的。
    楼上几位都说得那么清楚了,可 @meta 同学还是总结出“没看出来一定要使用extension的理由“,好吧。
    vixvix
        29
    vixvix  
       2013-09-01 12:11:46 +08:00
    extension跟category的区别是可以加ivar和properties. 这个特别属性使得在interface决定的情况下,不许要做大的修改你也可以给这个interface添加需要的功能。
    在大型项目或者外包项目中,interface在项目组或者公司间定下后通常就很少修改,这时你要扩展功能用extension就可以很容易的添加ivar或者properties。你或者可以使用外部singleton,但可读性和可维护性相对与extention就会差很多。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   953 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 81ms · UTC 21:25 · PVG 05:25 · LAX 13:25 · JFK 16:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.