
在 如何用代码禁用SpriteBuilder中创建的关节 一篇中提到了要想禁用一个关节就需要将其无效化。
然后我们在重新创建新关节时,可以参考该关节的原始参数。
但是代码中只能直接访问到bodyA和bodyB两个属性,anchorA、anchorB以及minDistance、maxDistance等4个属性无法直接访问到,书上称之为这些属性为私有属性(private property)。其实只要你包含了对应的头文件,就可以在自己的代码中直接引用它们。
由于这些属性值在例子中永不变化,所以书中使用了硬编码的方法来赋给新的关节,代码如下:
_lockJoint = [CCPhysicsJoint connectedDistanceJointWithBodyA:_lockJoint.bodyA
bodyB:_lockJoint.bodyB anchorA:ccp(0.0, -300.0)
anchorB:ccp(32.0, 32.0) minDistance:223.0
maxDistance:223.0];
但是需要知道的是,以上4个属性毫无疑问是存放在_lockJoint中的,只是无法直接访问到,下面就想办法从代码中直接取到这4个属性。
首先,obj-c中不存在真正的所谓私有方法,我们一般将不在interface中或在interface () 中声明的方法称之为私有方法。这种私有方法,不能直接通过[obj privagteMethod]的方式调用,编译器会抱怨一个错误的:告知类中没有该实例方法。
我们首先可以尝试用performSelector来取得该属性(因为不管啥属性其实也就是对应的2个方法;这里不考虑set方法,只考虑get方法)。
[obj performSelector:@selector(privateMethod)];
这样是可以调用到该私有方法,看上去很美 ;)
但是且慢,返回值不是id类型怎么办!?
对于返回值小于等于4bytes(因为在我的mac上sizeof(id)返回4)的方法,或许可以试试强制转换。但是double和CGPoint都大于4bytes,这样返回的值会被截断,结果肯定不正确。
我们可以看一下这4个属性在对于头文件中的声明:
@interface ChipmunkSlideJoint : ChipmunkConstraint
/**
Create an autoreleased slide joint between the two bodies with the given anchor points and distance range.
*/
+ (ChipmunkSlideJoint *)slideJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB min:(cpFloat)min max:(cpFloat)max;
/**
Initialize a slide joint between the two bodies with the given anchor points and distance range.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB min:(cpFloat)min max:(cpFloat)max;
/// The anchor point on the first body.
@property(nonatomic, assign) cpVect anchorA;
/// The anchor point on the second body.
@property(nonatomic, assign) cpVect anchorB;
/// The minimum allowed distance between anchor points.
@property(nonatomic, assign) cpFloat min;
/// The maximum allowed distance between anchor points.
@property(nonatomic, assign) cpFloat max;
@end
可以知道cpFloat和cpVect实际分别对应于double和CGPoint。
我们先来搞定返回值为double的属性(sizeof(double)为8)。大家知道调用对象的方法实际是向该对象发消息(performSelector内部也是如此),由此引出一个返回double的专有函数:
#import <objc/message.h>
objc_msgSend_fpret(instance,selector,...);
上面selector就是@selector(min)或者@selector(max),但是instance是神马呢?其实CCPhysicsJoint实例中有一个constraint属性,该属性又是另一个“私有”类ChipmunkConstraint的实例,所以我们要先取到constraint属性:
id cs = [_lockJoint performSelector:@selector(constraint)];
因为该私有方法正好返回一个id所以可以直接用performSelector来取得该属性。下面我们来取min和max的值:
double min = objc_msgSend_fpret(cs, @selector(min));
double max = objc_msgSend_fpret(cs, @selector(max));
that’s all!
接下来是返回CGPoint的anchorA、anchorB方法。
对于取得返回为结构这种情况,我们可以考虑用obj-c的invocation机制来完成。
首先用方法签名创建一个NSInvocation对象:
NSInvocation *invo = [NSInvocation invocationWithMethodSignature:[[Constraint class]
instanceMethodSignatureForSelector:@selector(anchorA)]];
这里的Constraint不可以用ChipmunkConstraint,而必须用其对应的子类ChipmunkSlideJoint。因为anchorA、anchorB方法是在这些子类中定义的。这个不像前面的向一个对象sendMsg的情况,前面会动态根据实际对象类型执行特定方法,这是在运行时完成的。而这里取得方法签名是在编译时完成的,如果该方法不在对应类类(即使在其子类中),instanceMethodSignaturForSelector会返回nil,从而使得invocationWithMethodSignature:抛出异常。
所以我们有:
Class Constraint = NSClassFromString(@"ChipmunkSlideJoint");
NSInvocation *invo = [NSInvocation invocationWithMethodSignature:[[Constraint class]
instanceMethodSignatureForSelector:@selector(anchorA)]];
[invo setSelector:@selector(anchorA)];
[invo setTarget:cs];
[invo invoke];
CGPoint pa;
[invo getReturnValue:&pa];
取anchorA和上面类似,不再赘述。