DoS*_*DoS 0 cocoa-touch objective-c
我不确定最有说服力的说法,但我会尽我所能.我创建了一个自定义类,它是一个具有一些属性的通用对象.我创建了几个子类来扩展它并使它们比超类更具体.所以为了举例,我会抛出一些通用的示例代码,这些代码可能是也可能不是正确的语法,只是为了说明我想要完成的事情.
@interface Vehicle : NSObject
@property (nonatomic) int wheels;
- (id)initWithNumberOfWheels:(int)wheels;
@end
Run Code Online (Sandbox Code Playgroud)
从那里我为同一个"汽车"和"卡车"创建一些子类,为课程提供更多细节.
@interface Car : Vehicle
@property (nonatomic) BOOL convertible;
@property etc...
@end
Run Code Online (Sandbox Code Playgroud)
和...
@interface Truck : Vehicle
@property (nonatomic) BOOL is4x4;
@property (nonatomic) int numberOfDoors;
@end
Run Code Online (Sandbox Code Playgroud)
所以这里有趣的地方.我想创建另一个分配这些对象的类,但我希望在init方法中确定车辆的"类型",但使用相同的@property变量.所以例如(再次,这只是给出可视化表示的所有垃圾代码)
Road.h
#import "Car.h"
#import "Truck.h"
@interface Road : NSObject
@property (strong, nonatomic) NotSureWhatToUseHereToMakeThisWork *myRide;
// doesn't work: @property (strong, nonatomic) id myRide;
// doesn't work: @property (strong, nonatomic) Vehicle *myRide;
- (id)initWithCars;
- (id)initWithTrucks;
@end
Run Code Online (Sandbox Code Playgroud)
Road.m
@implementation Road
- (id)initWithCars
{
//standard init code...
myRide = [[Car alloc] initWithNumberOfWheels:4];
myRide.convertable = NO;
}
- (id)initWithTrucks
{
//standard init code...
myRide = [[Truck alloc] initWithNumberOfWheels:6];
//yes, some trucks have more than 4 wheels
myRide.is4x4 = YES;
}
@end
Run Code Online (Sandbox Code Playgroud)
底线是如果我在@property中使用超类,它显然不会获得子类属性.基本上我想把所有这些保持为通用和可重用的.自从为汽车和卡车制作一个特殊的"公路"课程以来,它并没有成功.毕竟,道路是一条道路.无论如何我还在做什么?有没有更好的方法来做这样的事情?主要目的是使对象仅为特定情况继承特定属性.我不想制作额外的@properties的原因是我不希望那些可见,如果它们不适用于这种情况.
编辑:我添加了几个额外的片段,以显示我尝试过之前甚至发布这个无效的问题.
回答:如果有人好奇的话,正确的"答案"是CRD在"附录"中的答复.这工作的原因是类型"id"只能调用方法而不会继承属性.所以相反的解决方法(我这样说,因为我正在研究这个,得出结论这不是很好的编程,如果可能的话应该避免)将使用访问器方法来获取/设置属性.
id mySomethingObject = [[SomeClass alloc] init...];
[mySomethingObject setPropertyMethod]...; //sets value
[mySomethingObject propertyMethod]...; //gets value
Run Code Online (Sandbox Code Playgroud)
而不是试图使用......
mySomethingObject.property = ; //set
mySomethingObject.property; //get
Run Code Online (Sandbox Code Playgroud)
正如在正确答案中所述,如果您分配了"id"的课程没有响应该方法,您的课程将会崩溃.
你似乎混淆了许多问题.
首先是实例的类型与保存对实例的引用的变量的类型.当创建一个对象是一些特定类型的它和类型并没有改变[*].变量也有一个类型,并且也不会改变.子类型/继承允许您存储对某种类型的对象的引用T,在某种其他类型的变量中S,提供的S是超类型T.
其次是静态与动态类型.虽然Objective-C使用动态类型,但在某些操作中使用的实际对象类型是在运行时确定的,编译器本身使用静态类型,其中类型在编译期间确定,以帮助编写正确的程序.有时编译器静态检查只会产生警告,但在其他情况下,编译器将拒绝编译某些东西.特别是,属性引用是基于静态类型编译的.
在您的示例中,这意味着您不能直接引用Car由类型变量引用的对象的属性Vehicle *,即使您知道引用的对象是Car- 在编译时,所有已知的是它是a Vehicle.
解决方案是首先测试引用对象的实际类型,然后引入更精确类型的局部变量,或者使用大量的强制转换.例如:
// (a) create an object of type Car (for a Reliant Robin ;-))
// (b) create a variable of type Car and store in it a reference to the created Car
Car *myCar = [[Car alloc] initWithNumberOfWheels:3];
// Create a variable of type Vehicle and store in it the reference stored in myCar
// The created instance is *still* a Car
Vehicle *myRide = myCar;
// See if myRide is a Car and then do something
if ([myRide isKindOfClass:Car.class])
{
// create a variable of type Car to avoid having to continually cast myRide
Car *myCarRide = (Car *)myRide; // due to if above we know this cast is valid
if (myCarRide.isConvertible) ...
Run Code Online (Sandbox Code Playgroud)
要在没有中间变量的情况下执行此操作,请使
...
// See if myRide is a Car and then do something
if ([myRide isKindOfClass:Car.class])
{
if (((Car *)myCarRide).isConvertible) ...
Run Code Online (Sandbox Code Playgroud)
这说明了为什么中间变量方法更好!
作为最后一个例子,你写这样的initWithTrucks方法:
- (id)initWithTrucks
{
//standard init code...
Truck *myTruck = [[Truck alloc] initWithNumberOfWheels:6];
//yes, some trucks have more than 4 wheels
myTruck.is4x4 = YES;
// Store the reference to the created Truck in myRide
myRide = myTruck;
}
Run Code Online (Sandbox Code Playgroud)
HTH
附录
从您的评论看来,您可能正在寻找动态类型,并且不希望编译器执行任何静态类型.这是(部分)支持,但不使用属性的点表示法 - 您必须直接使用getter和setter方法.
首先,在Road你声明myRide是类型id:
@interface Road : NSObject
@property id myRide;
Run Code Online (Sandbox Code Playgroud)
的id类型意味着两件事(a)任何对象引用和(b)不静态检查对象上存在的方法.但是编译器必须知道某个对象上存在一个被调用的方法,并且它仍然会对该方法的参数执行静态类型检查 - 所以它不是完整的动态类型(但是你可以传递id类型表达式或声明你的方法来获取参数id课程类型......).
其次,您对属性的所有引用都直接使用getter或setter方法,并且不使用点表示法(对于非属性方法,您只需像往常一样调用它们).例如:
- (id)initWithTrucks
{
//standard init code...
myRide = [[Truck alloc] initWithNumberOfWheels:6];
//yes, some trucks have more than 4 wheels
[myRide setIs4x4:YES];
}
Run Code Online (Sandbox Code Playgroud)
如果您进行调用,例如[myRide setIs4x4:YES]并且myRide正在引用某个Car对象,那么您将收到运行时错误.
一般建议尽可能多地使用编译器的静态类型检查.
[*]我们将忽略任何运行时魔法,有龙.在普通代码中,对象永远不会改变类
| 归档时间: |
|
| 查看次数: |
1825 次 |
| 最近记录: |