This Section includes some basic idea about "Copy","Archiving","Blocks" and "Memory management".
> To support encoding and decoding of instances, a class must adopt the NSCoding protocol and implement its methods.
> An object being encoded or decoded is responsible for encoding and decoding its state.
> Methods for encoding are encodeWithCoder: or initWithCoder:
> The encodeWithCoder: method instructs the object to encode its state with the provided coder
An object can receive this method any number of times.
> The initWithCoder: message instructs the object to initialize itself from data in the provided coder.
It replaces any other initialization method and is sent only once per object.
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
_firstName = [coder decodeObjectForKey:ASCPersonFirstName];
_lastName = [coder decodeObjectForKey:ASCPersonLastName];
_height = [coder decodeFloatForKey:ASCPersonHeight];
}
return self;
}
If the superclass adopts the NSCoding protocol, you start by assigning of the return value of initWithCoder: to self:
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
}
the code in the body references the variable ‘price’ which is defined outside the block.
The output for the log statement is:
Ordering 10 units, final price is: $19.90
Now change the price and run the program
price = .99;
NSLog(@"Ordering %d units, final price is: $%2.2f", orderQuantity, finalPrice(orderQuantity));
The output for the block, with the price variable updated is:
Ordering 10 units, final price is: $19.90
To allow a variable
defined outside a block to be mutable, apply the ___block storage type
modifier:
Because we used the mutable version of the copy method we will be able to modify the contents of myString2. In doing so no change will occur to myString1 because that is an entirely different object.
If we attempted to use either of these copying methods on our own classes without implementing the <NSCopying> protocol the code will fail to run.
Encoding and Decoding Objects
> To support encoding and decoding of instances, a class must adopt the NSCoding protocol and implement its methods.
> An object being encoded or decoded is responsible for encoding and decoding its state.
> Methods for encoding are encodeWithCoder: or initWithCoder:
> The encodeWithCoder: method instructs the object to encode its state with the provided coder
An object can receive this method any number of times.
> The initWithCoder: message instructs the object to initialize itself from data in the provided coder.
It replaces any other initialization method and is sent only once per object.
Encoding
When an object receives an encodeWithCoder: message, it should encode all of its vital state, after forwarding the message to its superclass if its superclass also conforms to the NSCoding protocol.
encodeObject:forKey: method is used to encode id’s, scalars, C arrays, structures, strings, and pointers to any of these types.
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.firstName forKey:ASCPersonFirstName];
[coder encodeObject:self.lastName forKey:ASCPersonLastName];
[coder encodeFloat:self.height forKey:ASCPersonHeight];
}
This example assumes that the superclass of Person does not adopt the NSCoding protocol.
If the superclass of your class does adopt NSCoding, you should invoke the superclass’s encodeWithCoder: method before invoking any of the other encoding methods:
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
// Implementation continues.
}
encodeObject:forKey: method is used to encode id’s, scalars, C arrays, structures, strings, and pointers to any of these types.
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.firstName forKey:ASCPersonFirstName];
[coder encodeObject:self.lastName forKey:ASCPersonLastName];
[coder encodeFloat:self.height forKey:ASCPersonHeight];
}
This example assumes that the superclass of Person does not adopt the NSCoding protocol.
If the superclass of your class does adopt NSCoding, you should invoke the superclass’s encodeWithCoder: method before invoking any of the other encoding methods:
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
// Implementation continues.
}
Decoding an Object
In the implementation of an initWithCoder: method, the object should first invoke its superclass’s designated initializer to initialize inherited state, and then it should decode and initialize its state.
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
_firstName = [coder decodeObjectForKey:ASCPersonFirstName];
_lastName = [coder decodeObjectForKey:ASCPersonLastName];
_height = [coder decodeFloatForKey:ASCPersonHeight];
}
return self;
}
If the superclass adopts the NSCoding protocol, you start by assigning of the return value of initWithCoder: to self:
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
}
Blocks
Blocks are “anonymous functions” and can be defined inline.
Blocks capture read-only copies (unless defined with __block, more on that later) of local variables, similar to “closures” in other languages.
Block variable
To define a block variable, the ^ operator is used.
BOOL ( ^isInputEven)(int) = ^(int input)
{
if(input % 2 == 0)
return YES;
else
return NO;
}
Call to a block is similar to c function call
int x = -101;
NSLog(@"%d %@ number", x, isInputEven(x) ? @"is an even" : @"is not an even");
Output is:
-101 is not an even number
Blocks and variable scope
float price = 1.99;
float (^finalPrice)(int) = ^(int quantity)
{
// Notice local variable price is accessible in the block
return quantity * price;
};
float (^finalPrice)(int) = ^(int quantity)
{
// Notice local variable price is accessible in the block
return quantity * price;
};
the code in the body references the variable ‘price’ which is defined outside the block.
The output for the log statement is:
Ordering 10 units, final price is: $19.90
Now change the price and run the program
price = .99;
NSLog(@"Ordering %d units, final price is: $%2.2f", orderQuantity, finalPrice(orderQuantity));
The output for the block, with the price variable updated is:
Ordering 10 units, final price is: $19.90
Using ___block Storage
Modifier
___block float price = 1.99;
float (^finalPrice)(int) = ^(int quantity)
{
return quantity * price;
};
int orderQuantity = 10;
price = .99;
NSLog(@"With block storage modifier - Ordering %d units, final price is:
$%2.2f", orderQuantity, finalPrice(orderQuantity));
> The output for the
block, with the price variable updated is:
> With block storage
modifier – Ordering 10 units, final price is: $9.90
Passing block as parameter
> Since blocks are
“variables” you can pass blocks to methods and functions.
- (NSArray
*)each:(void (^)(id))block;
- (NSArray
*)each:(void (^)(id object))block
{
for(id mObject in self)
block(mObject);
return self;
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool =
[[NSAutoreleasePool alloc] init];
NSArray *array =
[NSArray arrayWithObjects:@"Number
one", @"Number two", nil];
[array each:^(id
object) {
NSLog(object);
}];
[pool drain];
return 0;
}
Copying in Objective C
Most classes are derived from the NSObject base class. those classes inherit a number of useful methods designed specifically for creating, managing and manipulating objects Two such methods are the copy and mutableCopy methods.
These methods use something called the <NSCopying> Protocol. Classes from the Foundation Framework will typically already be compliant with the <NSCopying> Protocol. We can, therefore, simply call the copy or mutableCopy methods to create a copy of an object:
These methods use something called the <NSCopying> Protocol. Classes from the Foundation Framework will typically already be compliant with the <NSCopying> Protocol. We can, therefore, simply call the copy or mutableCopy methods to create a copy of an object:
NSString *myString1 = @"Hello";
NSString *myString2;
myString2 = [myString1 mutableCopy];
On execution of the mutableCopy method in the above example we will have two independent string objects both of which contain the same string.Because we used the mutable version of the copy method we will be able to modify the contents of myString2. In doing so no change will occur to myString1 because that is an entirely different object.
If we attempted to use either of these copying methods on our own classes without implementing the <NSCopying> protocol the code will fail to run.
<NSCopying> Protocol and copyWithZone Method Implementation
The first step in implementing the <NSCopying> protocol is to declare that the class conforms to the protocol.
@interface BankAccount: NSObject <NSCopying>
Also in the implementation we need to declare that the class includes a method named copyWithZone that returns a new object and accepts the zone of the source object as an argument.
@interface BankAccount: NSObject <NSCopying>
{
double accountBalance;
long accountNumber;
}
-(void) setAccount: (long) y andBalance: (double) x;
-(double) getAccountBalance;
-(long) getAccountNumber;
-(void) setAccountBalance: (double) x;
-(void) setAccountNumber: (long) y;
-(void) displayAccountInfo;
-(id) copyWithZone: (NSZone *) zone;
@end
In our @implementation section we now need to write the code for our copyWithZone method.
This method creates a new BankAccount object, copies the values of the instance variables (in this case accountBalance and accountNumber) and returns a pointer to the new object:
-(id) copyWithZone: (NSZone *) zone
{
BankAccount *accountCopy = [[BankAccount allocWithZone: zone] init];
[accountCopy setAccount: accountNumber andBalance: accountBalance];
return accountCopy;
}
int main (int argc, const char * argv[])
{
@autoreleasepool
{
BankAccount *account1;
BankAccount *account2;
account1 = [BankAccount alloc]init];
[account1 setAccountBalance: 1500.53];
[account1 setAccountNumber: 34543212];
[account1 displayAccountInfo];
account2 = [account1 copy];
[account2 displayAccountInfo];
}
return 0;
}
Now when executed, the above code creates a copy of the object referenced by account1 and assigns a pointer to the new object to variable account2.
Performing a Deep Copy
NSArray *myArray1;
NSArray *myArray2;
NSMutableString *tmpStr;
NSMutableString *string1;
NSMutableString *string2;
NSMutableString *string3;
string1 = [NSMutableString stringWithString: @"Red"];
string2 = [NSMutableString stringWithString: @"Green"];
string3 = [NSMutableString stringWithString: @"Blue"];
myArray1 = [NSMutableArray arrayWithObjects: string1, string2, string3, nil];
We now have an array name myArray1 that contains as elements three variables that each point to a string object.
We could now create a copy of that array and assign it to variable pointer myArray2:
myArray2 = [myArray1 copy];
The myArray2 object is a separate object, but the elements it contains still point to the same three string objects.
tmpStr = [myArray1 objectAtIndex: 0];
objectAtIndex: 0];
[tmpStr setString: @"Yellow"];
NSLog (@"First element of myArray2 = %@", [myArray2 objectAtIndex: 0]);
When compiled and executed, the NSLog call will display the following output:
First element of myArray2 = Yellow
Clearly when we change the object pointed to by element 0 of myArray1 we were also changing the object pointed to by element 0 or myArray2.
In order to create entirely new instance objects we need to perform a deep copy.
This can be achieved by writing the object and its constituent elements to an archive and then reading back into the new object.
NSArray *myArray1;
NSArray *myArray2;
NSMutableString *tmpStr;
NSMutableString *string1;
NSMutableString *string2;
NSMutableString *string3;
NSData *buffer;
string1 = [NSMutableString stringWithString: @"Red"];
string2 = [NSMutableString stringWithString: @"Green"];
string3 = [NSMutableString stringWithString: @"Blue"];
myArray1 = [NSMutableArray arrayWithObjects: string1, string2, string3, nil];
buffer = [NSKeyedArchiver archivedDataWithRootObject: myArray1];
myArray2 = [NSKeyedUnarchiver unarchiveObjectWithData: buffer];
tmpStr = [myArray1 objectAtIndex: 0];
[tmpStr setString: @"Yellow"];
NSLog (@"First element of myArray1 = %@", [myArray1 objectAtIndex: 0]);
NSLog (@"First element of myArray2 = %@", [myArray2 objectAtIndex: 0]);
First element of myArray1 = Yellow
First element of myArray2 = Red
No comments:
Post a Comment