RSS
 

Archive for the ‘Programming’ Category

Memory Management Part I: Objective-C Object Ownership

16 May

Proper memory management by an application is key to stability of an application. Doing it incorrectly can cause a myriad of bad things to happen! The side effects can include corrupting application data, crashing the application, locking up use of the computer or device, or even crashing the system or causing it to reboot.  Thus, memory management concerns are always in the back of the mind of every programmer (and, if they are unfortunate, the front of their mind), especially when they learn a new language. This article introduces programming issues related to memory management. In my next article I will get more technical with best memory management practices in programming iOS/CocoaTouch View Controllers.

In older, procedural programming languages that allow memory allocation (non-object based languages such as C or  Pascal), managing memory was pretty straight forward:

int appFunction(int size)
{
	int *array = malloc(size * sizeof(int));
	⋮
	free(array);   // Done with the dynamically allocated memory
} // appFunction()
Example: C-language memory allocation.

With object-oriented programming, application designers tend to structure their designs as a fluid interaction of objects. The syntax of object-oriented languages (such as C++ or Objective-C) helps programmers to avoid memory management mistakes easier than procedural languages, particularly in more complex application designs.

Since Objective-C was invented along with the APIs and frameworks used for environments like iOS and Mac OS X, Apple assumes its use with a class hierarchy and, for all practical purposes, a dependence on the NSObject base-class. This class determines a whole gamut of conventions that are nearly indistinguishable from the Objective-C language itself, so it is hardly worth discussing the use of Objective-C without NSObject.  While it looks complex, the important thing is that the allocation and deallocation is handled at well-known locations in the code that will be used for other purposes, the initializer and deinitializer.

- (id)init:(int) size
{
	self = [super init];
	if (self)
		_array = [[NSArray alloc] init];
	return self;
} // -init
	⋮
- (void)dealloc
{
	[_array release];
	[super dealloc];   // Done with the dynamically allocated memory
} // -dealloc
Example: Objective-C memory handling

Just as, with C, where you need to internalize when you should call malloc() and free(), with Objective-C  you should internalize “object ownership” and when to send retain and release. These retain/release messages (sending a message is analogous to a function or method call, in other languages) implement reference-counting—a way of keeping track of how many objects are referring to the object.

Internalize this rule: If you do allocretain, or copy on an object, you own it and it is your job to release it… otherwise it isn’t.

In Objective-C, objects allocate other objects for its use or other’s use. These allocations are memory allocations. Cocoa (used in Mac OS X) and CocoaTouch (the Cocoa variant used for iOS) programming frameworks assume a clear understanding of who owns an object. Any number of objects can retain ownership another, either explicitly or implicitly. If one object allocates another, then there is an implicit ownership of that object.

stringOwner implicitly owns the allocated object, so it must release it.

	NSString *stringOwner = [[NSString alloc] init];
	⋮
	[stringOwner release];

tempString does not get ownership, but it can use it for the duration of the method.

	NSString *tempString = [NSString string];

stringOwner has claimed ownership via retain, so it must release it.

	NSString *stringOnwer = [[NSString string] retain];
	⋮
	[stringOwner release];

If an object is passed as a parameter to another method, it is possible that the other method may claim ownership of the object. In this case, the sender (AKA “caller” in other languages) can release the object if it won’t need it, later.

	NSString *stringOnwer = [[NSString string] retain];
	⋮
	[existingArray addObject:stringOwner];  // array is also, now, an owner
	⋮
	[stringOwner release];  // Not needed by this method (or class any longer)

The problem is that, despite Apple’s simple set of rules (or, better yet, the Rules of Thumb), it is often not clear which of their APIs retain ownership of the objects passed in, even by reading the official documentation. I usually find that if I am at all unsure, I need to google it—which often leads to the most reliable advice coming from Stack Overflow.

Apple must know this because they provide their excellent (though not perfect) Instruments tool that works with their Xcode development environment to help ferret out memory bugs and leaks.

Next, I will write about memory usage as it relates to iOS CocoaTouch View Controllers (because that also isn’ve very apparent).

Resources — The Basics

 

Frameworks: Beyond the Nuts and Bolts

06 May

Without frameworks our “big ideas” would still be stuck in the pre-industrial age. —Bill Lee, Berlin, 2008

I have been on a long hunt for a PHP framework so I was going to write up an article about PHP frameworks, but then I thought I should back up a bit and talk about what a framework is and why you should use one.

Software driven computing has existed for over 60 years. In the early days computer programming was performed using long series of numbers to represent the electronic switches tell a computer what to do—computers still work this way. Fortunately no one has to program computers by speaking computer-ese numbers anymore. With that kind of tedious programming, the most advanced programs would be tic-tac-toe games.

But numbers are the nuts-and-bolts of a computer’s operation. Designing a sky-scraper while having to dwell on every nut and bolt of its construction might limit us to working in ten story buildings. It is vitally important in designing complex systems to be able to think at more abstract, level. Programming using numbers quickly led to “assembly language” as mnemonic way of specifying those numbers, then to “third-generation” programming languages (such as C, C++, Pascal, Fortran) which allow programs to be written without regard to the numbers that the computers can actually interpret. This frees software designers to think about problem-solving without being encumbered by the tediousness of the numerical “nuts-and-bolts” needed by the computers that run the programs.

Constructing a building does not occur in isolation; its context involves the skills and machinery of those involved as well as governmental and environmental infrastructure. Over the past 30+ years, the complexity of computing has given rise to very advanced operating systems that define the context in which a program runs. Add to that, the interactions between a program and other programs or data that exist across a network or even the internet and the complexity of issues add to the tediousness of dealing with nuts and bolts. Again, if a software designer has to dwell on the minutia of all these elements, modern complex, powerful software would never be completed.

A framework attempts to encapsulate the complexities of designing and implementing software. A good framework allows software design to occur more abstractly, unencumbered by the tediousness of the contextual details of  the environment. Said a different way, a good framework allows the software designers to focus on the application’s purpose rather than the complexities of implementation.

Software is constructed in layers or components. Encapsulation is one of my favorite concepts in software design and implementation. It embodies the idea that there are specifics at every level or component within the software system that need not be exposed outside that implementation for that level. There are two key side-effects of this: (1) it prevents parts of the program outside the component from messing with or depending on implementation details which leads to (2) each component’s implementation can be changed or improved without fear of adversely affecting use of the component in unexpected ways. This leads to more stable, maintainable products.

Good programmers embed encapsulation within their design and implementation. Many object-oriented programming languages attempt to formalize this concept so that it is easy to do this. Well designed frameworks do the same, “protecting” some parts of the program from complex or variable implementation details.

There are good frameworks and there are bad ones. In a later article, I will discuss some of the issues to consider in picking a framework.

What have been your experiences in using programming frameworks?