小弟初学objective-c,对于extension的作用一直无法理解,请各位前辈指点一二。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
meta
V2EX    C

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

  •  
  •   meta 2013-08-31 09:45:05 +08:00 2659 次点击
    这是一个创建于 4437 天前的主题,其中的信息可能已经有所发展或是发生改变。
    看到很多书上都说,扩展可以用来实现类的私有实例变量和方法,那么把方法从.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 &nbp;
       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     Solana     3204 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 04:56 PVG 12:56 LAX 21:56 JFK 00:56
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86