Transform variable not being Retained in Memory?

So I have a serializable class that starts off like this:

[System.Serializable]
public class ThinAirfoil {
	public string name;
	public Transform airfoilObject;

And I create instances of it in the inspector using this variable on another script:

public ThinAirfoil[] airfoilObjects;

Every frame the other script calls a function that tries to access the instances’ airfoilObject variables. This works fine within Unity Editor… but when I try to run it in Xcode, I get a “exc_bad_access” error - presumably because the variable has somehow cleared from memory automatically.

What gives? How do I make it retain the variable?

I finally managed to get script debugging to actually build and got this:
Unhandled Exception: System.NullReferenceException: A null value was found where an object instance was required.
at ThinAirfoil.UpdateRelativeAirVelocity () [0x00000] in ThinAirfoil.cs:121
at ThinAirfoil.UpdateLift () [0x00000] in ThinAirfoil.cs:90
at ThinAirfoil.UpdateEvents () [0x00000] in ThinAirfoil.cs:52
at BirdPhysicsHandler.FixedUpdate () [0x00018] in BirdPhysicsHandler.cs:51

which basically just says what I already described above

Also - I included a Debug.Log() that should print the transform variable an " is present!!!", but it only seems to work on the first time through the loop that calls it:

So the foreach loop runs once, works fine, then somehow before it runs a second time, the other variable gets deleted. It runs the second time through loop and the variable is gone. There should be a LeftAirfoil and a RightAirfoil.

Can you post a snippet with your loop?

Certainly, Here is the serializable class, the main script, and a screenshot of the main script component in the inspector.

In the inspector screenshot, “Middle Right Wing” is the variable that gets deleted.

Note that this does not happen in the editor, it only happens when running on iOS devices/simulators.

The serializable class:

using UnityEngine;
using System.Collections;

[System.Serializable]
public class ThinAirfoil {
	public string name;
	public Transform airfoilObject;
	public GameObject airfoilGameObject;
	
	//-----------------------------------------------
	
	private BirdPhysicsHandler birdPhysicsHandler;
	private Rigidbody handlerRigidbody;
	
	//-----------------------------------------------
	//-----------------------------------------------
	//DIMENSIONAL VARIABLES
	public float wingspan = 1;
	public float averageChord = 0.22f;
	private float aspectRatio;
	private float planformArea = 0.0f;
	
	//-----------------------------------------------
	//DIMENSIONAL FUNCTIONS
	
	//UPDATE THE PLANFORM AREA
	void UpdatePlanformArea () {
		planformArea = wingspan*averageChord;
		UpdateAspectRatio();
	}
	
	//UPDATE THE ASPECT RATIO
	void UpdateAspectRatio () {
		aspectRatio = (wingspan*wingspan)/planformArea;
	}
	
	//-----------------------------------------------
	//-----------------------------------------------
	//AWAKE EVENTS
	
	public void AwakeEvents (BirdPhysicsHandler passedHandler) {
		birdPhysicsHandler = passedHandler;
		handlerRigidbody = birdPhysicsHandler.thisRigidbody;
		
		UpdatePlanformArea();
	}
	
	//-----------------------------------------------
	//-----------------------------------------------
	//UPDATE EVENTS
	
	public void UpdateEvents () {
		UpdateLift();
		UpdateDrag();
		
		ApplyCombinedForces();
		
		#if UNITY_EDITOR
		DrawVectors();
		#endif
	}
	
	//-----------------------------------------------
	//-----------------------------------------------
	//LIFT VARIABLES
	
	private Vector3 relativeAirVelocity;
	
	private float angleOfAttack;
	
	private float airspeedVelocity;
	
	private float dynamicPressure;
	
	public bool isCambered = true;
	
	public float zeroAOALiftCoefficient = 1.2f;
	
	private float liftCoefficient;
	
	private float lift;
	
	//-----------------------------------------------
	//-----------------------------------------------
	//LIFT
	
	//CALCULATE THE VALUE OF LIFT
	void UpdateLift () {
		//Update local-space (relative) velocity
		//using global-space velocty of this object
		UpdateRelativeAirVelocity();
		
		//Angle of Attack
		UpdateAngleOfAttack();
		
		//Airspeed Velocity
		//(3D velocity of local-space air moving past wing)
		//(Non-directional, since AoA handles direction)
		airspeedVelocity = relativeAirVelocity.magnitude;
		
		//Dynamic Pressure
		dynamicPressure = 0.5f*birdPhysicsHandler.airDensity*airspeedVelocity;
		
		//Lift coefficient using Thin-Airfoil Theory approximation
		liftCoefficient = 2.0f*Mathf.PI*(angleOfAttack*Mathf.Deg2Rad);
		//Handle Airfoil Shape
		if (isCambered) {
			//Apply Cambering Effect
			liftCoefficient += zeroAOALiftCoefficient;
		}
		
		//Relative Lift Value
		lift = dynamicPressure*planformArea*liftCoefficient;
	}
	
	//-----------------------------------------------
	
	//CALCULATE THE RELATIVE AIR VELOCITY
	//(convert total air velocity vector to the object's local-space coordinates)
	void UpdateRelativeAirVelocity () {
		//Convert global-space velocity to local-space (relative-space)
		relativeAirVelocity = airfoilObject.InverseTransformDirection(birdPhysicsHandler.totalGlobalAirVelocity);
	}
	
	//CALCULATE THE ANGLE OF ATTACK
	//(get the angle of attack using the actual relative, local-space, air velocity vector)
	void UpdateAngleOfAttack () {
		//(Only use y and z because we don't care about side-to-side for AoA)
		if (relativeAirVelocity.z >= 0) {
			angleOfAttack = Vector2.Angle(Vector2.right, new Vector2(relativeAirVelocity.z, relativeAirVelocity.y));
		} else {
			//If the object is moving backwards,
			//we change the AoA to act like the
			//back of the airfoil is now the front.
			angleOfAttack = -Vector2.Angle(-Vector2.right, new Vector2(relativeAirVelocity.z, relativeAirVelocity.y));
			
			//!!! - This is where a stall would trigger in the gameplay
		}
		
		//If the air velocity is coming from above the airfoil,
		//then make the AoA negative (pointing downward relative
		//to the passing air)
		if (relativeAirVelocity.y > 0) {
			angleOfAttack *= -1;
		}
	}
	
	//-----------------------------------------------
	//-----------------------------------------------
	//DRAG VARIABLES
	
	public float oswaldEfficiencyNumber = 0.9f;
	
	private float drag = 0.0f;
	
	//-----------------------------------------------
	//-----------------------------------------------
	//DRAG
	
	void UpdateDrag () {
		drag = (lift*lift)/(dynamicPressure*planformArea*Mathf.PI*oswaldEfficiencyNumber*aspectRatio);
	}
	
	//-----------------------------------------------
	//-----------------------------------------------
	//USE FORCES
	
	private Vector3 combinedForces;
	
	//APPLY COMBINED FORCES
	void ApplyCombinedForces () {
		combinedForces = GetLiftVector();
		combinedForces += GetDragVector();
		
		//Apply force
		//(converts it to world-space because
		//AddForceAtPosition doesn't have a
		//local-space variant)
		handlerRigidbody.AddForceAtPosition(airfoilObject.TransformDirection(combinedForces), airfoilObject.position);
	}
	
	//-----------------------------------------------
	
	//LIFT FORCE
	Vector3 GetLiftVector () {
		//Gets the lift in the direction of the
		//cross product of the local airflow vector
		//and the local "right" direction vector
		//(this makes the lift perpendicular to the airflow)
		return lift*Vector3.Cross(relativeAirVelocity, Vector3.right).normalized;
	}
	
	//DRAG FORCE
	Vector3 GetDragVector () {
		//Gets drag force in the opposite of the airflow vector
		//(because drag is applied as a pushing force,
		//not calculated as a velocity)
		return drag*(-relativeAirVelocity.normalized);
	}
	
	#if UNITY_EDITOR
	//-----------------------------------------------
	//-----------------------------------------------
	//DIAGNOSTICS
	
	public bool drawVectors = false;
	
	void DrawVectors () {
		if (drawVectors) {
			//Lift
			Debug.DrawRay(airfoilObject.position, airfoilObject.TransformDirection(lift*Vector3.Cross(relativeAirVelocity, Vector3.right).normalized), Color.green);
			//Drag
			Debug.DrawRay(airfoilObject.position, airfoilObject.TransformDirection(drag*(-relativeAirVelocity.normalized)), Color.red);
			//Forward
			Debug.DrawRay(airfoilObject.position, airfoilObject.forward, Color.yellow);
		}
	}
	#endif
}

The main script:

using UnityEngine;
using System.Collections;

public class BirdPhysicsHandler : MonoBehaviour {
	public ThinAirfoil[] airfoilObjects;
	
	public bool useWind = true;
	
	public Vector3 initialLocalVelocity = Vector3.forward;
	
	//-----------------------------------------------
	//-----------------------------------------------
	//EFFICIENCY VARIABLES
	
	[HideInInspector]
	public Rigidbody thisRigidbody;
	private Transform thisTransform;
	
	//-----------------------------------------------
	//-----------------------------------------------
	//AWAKE
	
	void Awake () {
		thisRigidbody = rigidbody;
		thisTransform = transform;
		
		//Set center of mass to align with the wings
		//!!! might want to set to custom position based on wing position
		rigidbody.centerOfMass = new Vector3(0, 0, 0);
		
		//Set Initial Local Velocity
		thisRigidbody.velocity = thisTransform.TransformDirection(initialLocalVelocity);
		
		//Run Airfoil Objects' Awake Events
		foreach (ThinAirfoil thinAirfoil in airfoilObjects) {
			thinAirfoil.AwakeEvents(this);
		}
	}
	
	//-----------------------------------------------
	//-----------------------------------------------
	//UPDATE
	
	void FixedUpdate () {
		//Update applied wind
		//(the motion of the wind relative to the object in global space)
		UpdateTotalGlobalAirVelocity();
		
		//Run Airfoil Objects' Update Events
		foreach (ThinAirfoil thinAirfoil in airfoilObjects) {
			thinAirfoil.UpdateEvents();
		}
		
		ApplyThrust();
		
		#if UNITY_EDITOR
		DrawVectors();
		#endif
	}
	
	//-----------------------------------------------
	//-----------------------------------------------
	//LIFT VARIABLES
	
	//Density of the air
	[HideInInspector]
	public float airDensity = 1.22521f; //Make this change with altitude
	
	[HideInInspector]
	public Vector3 totalGlobalAirVelocity;
	
	//-----------------------------------------------
	//LIFT CALCULATIONS
	
	//CALCULATE TOTAL GLOBAL AIR VELOCITY
	//(velocity vector of the air moving past the object in global-space coordinates)
	void UpdateTotalGlobalAirVelocity () {
		totalGlobalAirVelocity = thisRigidbody.velocity;
		
		if (useWind) {
			totalGlobalAirVelocity += WindGlobals.relativeGlobalWindVelocity;
		}
	}
	
	//-----------------------------------------------
	//-----------------------------------------------
	//THRUST
	
	private Vector3 combinedForces;
	
	//APPLY COMBINED FORCES
	void ApplyThrust () {
		//Apply force
		thisRigidbody.AddRelativeForce(GetThrustVector());
	}
	
	//-----------------------------------------------
	
	//THRUST FORCE
	public float forwardThrust = 1.0f;
	public float upwardThrust = 0f;
	
	Vector3 GetThrustVector () {
		//Gets thrust as a relative vector
		return new Vector3(0, upwardThrust, forwardThrust);
	}
	
	#if UNITY_EDITOR
	//-----------------------------------------------
	//-----------------------------------------------
	//DIAGNOSTICS
	
	public bool drawVectors = false;
	
	void DrawVectors () {
		if (drawVectors) {
			//Thrust
			Debug.DrawRay(thisTransform.position, thisTransform.TransformDirection(new Vector3(0, upwardThrust, forwardThrust)), Color.cyan);
			//Gravity
			Debug.DrawRay(thisTransform.position, Vector3.up*Physics.gravity.y, new Color(1, 0.5f, 0, 1));
			//Global Air Velocity
			Debug.DrawRay(thisTransform.position, totalGlobalAirVelocity, Color.white);
		}
	}
	
	#endif
}

709608--25635--$Screen Shot 2011-10-07 at 9.43.13 PM.png

A-HA! I figured it out. So you may notice that in the ThinAirfoil class, there’s a bool called drawVectors, which is wrapped in #if UNITY_EDITOR tags.

Apparently when this variable is serialized in the inspector, it caches the value of this variable for each instance of the class. However, when Unity builds, it does not remove the cached state of that variable - even though it doesn’t compile that variable. So this means that the built version of the object has a cached value for a variable that no longer exists.

Because of this, when it got to the end of Middle Left Wing, it got confused and (possibly) started offsetting the associations of values to variables. This meant that when it got to the next instance of ThinAirfoil, things were already screwy, so the variables in that instance were just cleared out.

So this looks like a bug as far as I’m concerned - seems like Unity should be aware that variables are no longer present and no longer need to be cached at build time. I’ve already filed the bug report.