奇葩的OBJC

先上代码(xcode 4.5):


// GGObjectA.h
#import <Foundation/Foundation.h>
@interface GGObjecA : NSObject
- (NSNumber *)something;@end 
// GGObjectB.m
#import "GGObjecA.h"
@implementation GGObjecA
- (NSNumber *)something{
    return@1 ;
}
- (void)dealloc
{
    NSLog(@"[A]the something:%@", self.something);
    [super dealloc];
}@end 

// GGObjectB.h
#import <Foundation/Foundation.h>
#import "GGObjecA.h"
@interface GGObjectB : GGObjecA
@property(nonatomic, retain) NSString *something;@end 

// GGOBjectB.m
@implementation GGObjectB
- (void)dealloc
{
    NSLog(@"[B]the something:%@", self.something);
    [super dealloc];
}@end 
测试运行的代码
    GGObjectB *gg = [[GGObjectBalloc] init];
// 这里的tmp是特意搞复杂一点为了,体现测试效果
    NSString *tmp = [[NSStringalloc] initWithFormat:@"%@", @"2"];
    gg.something = tmp;
    [tmp release];

    [gg release];
打印什么?
[B]the something:2
[A]the something:2

如你所愿,因为是多态,对属性something的调用都是调用的GGObjectB的方法,这没什么,OO特性都是这样的。
奇葩的是:
1. 重载规则不匹配,可是编译器不告警。GGObjectB的something属性,相当于编译器自动产生了
- (NSString *)something;
- (void)setSomething:(NSString *)aSomething;
那么,something方法就是一个只有返回值不同的重载函数(原谅我用C++的术语,OBJC叫消息吧)。

只有返回值不同的函数重载不应该被允许才对。

更正:这种情况不属于overload而是overwrite,重写是允许不同返回类型的。

2. 眼睛锐利的人肯定看出来上面GGObjectB是有内存泄露的,因为something是retain的,所以应该在dealloc的时候release一下。 那么,我们就release一下吧,GGObjectB的代码就变成这样:
- (void)dealloc
{
    [_something release];
    NSLog(@"[B]the something:%@", self.something);
    [super dealloc];
}
再跑一下,阿!非法操作了。
加个NSZombieEnabled=YES再跑一下:

*** -[CFString respondsToSelector:]: message sent to deallocated instance

哦,知道问题了,在GGObjetA的dealloc里面又访问something属性,但是,这时候访问的是GGObjectB的。
(C++应该有原则就是析构里面不要访问虚函数,好吧,这个我知道了,但是objc里面全都是虚函数,属性也是虚函数,要没法避免了呀)
最后,告诉大家我是怎么发现这个问题的:某天我实现了一个类,它有一个叫hash的属性,于是程序莫名其妙挂了,然后我找了很久很久,发现NSObject有一个- (NSUInteger)hash;
我猜测NSObject的dealloc方法是会调用这个hash去做些事情的。

现在我就纠结了,我们得如何防范这些陷阱呢?基类里面的属性那么多,方法那么多,我都要非常小心不要和他们重复吗?

你可能感兴趣的:(奇葩的OBJC)