EOFault

From EOFWiki
Jump to navigationJump to search

What an EOFault is[edit]

An EOFault is a temporary stand in to lazily initialize an object. The EOFault intercepts all message and initializes the object on first use. After the object is properly initialized the EOFault vanishes. A specialized EOFault could for example just return the name of a file, but will load the data of the file, when it's data is accessed. The advantage of EOFaults are that you don't need to handle lazy loading inside your classes.

What EOFault does, technically[edit]

You can turn a regular object into an EOFault and back into it's original object by "firing" the fault. This can be done on the same object multiple times if so desired.

FaultTransformation.png

At the time of the transformation into an EOFault the isa pointer and therefore the class of the object is swizzled to the EOFault class. The storage area typically occupied by the first instance variable is stored in the EOFaultHandler. The EOFaultHandler also memorizes the current retainCount of the object. Finally the EOFaultHandler is stored in the fault, overwriting the first instance variable.

When a fault is fired, this transformation is reversed. The instance variable storage is restored from the EOFaultHandler and the isa is swizzled back to the original class. Finally in a quite involved process it's made sure, that the retainCount of the object is correct.

What problem can EOFault solve ?[edit]

Imagine a case where your objects have relationships between each other. Lets say a person has relatives, which in turn have other relatives. Your person object is defined as having relations to his relatives. And a relative is of course also a person. So your person object may look like this:

@interface Person : NSObject
{
   NSMutableArray   *relatives_;
}

- (NSArray *) relatives;
- (void) addToRelatives:(Person *) other;
- (void) removeFromRelatives:(Person *) other;

There are two traditional approaches to handle such an object, if it gets persisted in a database.

  • deep fetching: whenever a person is fetched from the database, it will also fetch all its relatives from the database. Those in turn may have to fetch also their relatives. By the theory of Six Degrees Of Separation you would end up with the whole database in your memory. An alternative is to cut short the recursion at some point and let the application programmer deal with incomplete objects. These incomplete objects would be Persons that would not know about their relatives, without additional logic (Footnote Incomplete Persons).
  • fetch logic in the accessors: the accessors do not manipulate the array directly, but instead fetch from the database. The code might look something like this:
-(NSArray *) relatives
{
   NSArray   *array;

   if( ! relatives_)
   {
      relatives_ = [NSMutableArray new]:
      array      = [self _fetchObjectsForRelationshipSelector:_cmd]; // _cmd == @selector( relatives)
      [relatives_ addObjectsFromArray:array];
   }
   return( relatives_);
}

where _fetchObjectsForRelationshipSelector: is an assumed function, that does the right thing.

EOF solves the problem by creating a special fault array in the place of the regular NSMutableArray. This special array uses the forwarding mechanism of NSObject to intercept all messages. The first time a message is received that acesses an array member, the contents of the array are fetched and the special fault array transforms itself into a regular array.

This is done by swizzling of the isa pointer so the actual address of the object doesn't change.

In common day to day operations this involves no coding effort on the application programmer side. EOF does the creation of the faults for you during fetches and also the resolution of the fault using one of its EOFaultHandlers.

Note: With Garbage Collection technology it should be possible to replace objects with objects at a different address.

Typical Applications[edit]

Generally EOFault should improve the load time dramatically of

  • Data stored in several files
  • Data stored in several tables in a database

Limitations of EOFault[edit]

The object that should be faulted must have enough instance variable space for two pointers at least. So as example, in Mac OS X 10.4 you could not turn a regular Apple Foundation NSMutableArray array into a EOFault because it is too small.

There is no provision in the standard EOFault mechanism to incrementally load the needed data. It's all or nothing. MulleEOF has an EOPartialFault, which mitigates the problem somewhat.

Why faulting is like a delayed -init or why it is not[edit]

Picturing the firing of a fault to be a delayed init can be helpful yet misleading at the same time. bla bla bla

An example use of EOFault[edit]

See PropertyListFaultHandler example

Pitfalls when using EOFault[edit]

An EOFault is transparent as long as you are messaging the object "properly". IMP caching of EOFault methods is safe, except when you cache via the class.

this works

  imp = [fault methodForSelector:@selector( name)];
  // fault will have fired
  (*imp)( fault, sel);

but this will not

  imp = [[fault class] instanceMethodForSelector:@selector( name)];
  // imp will now contain the address of the class method the fault will clear to
  (*imp)( fault, sel);
  // but here fault is still a fault

Solutions:

  • Use -self to fire the fault beforehand.
  • Check with +[EOFault isFault:fault] or MulleEOIsFault( fault) before IMP caching via class.

Related Concepts[edit]

Questions and Answers[edit]