self is the object you're sending a message to. For instance, if you call something like [foo doSomething], in the doSomething method, self will be equal to foo. It's a hidden argument passed to methods.
It might be confusing that self is not read-only in Objective-C. For instance, in the initializer, as you may see, you actually override self:
if( self = [super init] ){
This is because [super init] is actually entitled to return a completely different object. However, you're just changing the value of a variable passed as an argument; you're not changing the value in the scope that called the method.
Foo* foo = [[Foo alloc] init];
// ... may be different of...
Foo* foo = [Foo alloc];
Foo* bar = [foo init];
In this example, in the second case, foo and bar may actually point to two distinct objects. Both alloc and init return a pointer to an object, and technically they could be different (though the only one you'll want to keep is the one init returned, since the object pointed to by alloc's return value isn't ready for use).
As mentioned above, self is actually one of two hidden arguments methods receive. The other hidden argument is named _cmd and contains the selector used to call the method. You rarely need it.
This means that whenever you see this:
id bar = [foo doSomethingWithInt:5];
You could (symbolically) resolve it to a function call similar to this:
id bar = Foo_doSomething(foo, @selector(doSomethingWithInt:), 5);
So self is really just an argument.