Presentation is loading. Please wait.

Presentation is loading. Please wait.

Memory Management and Automatic Reference Counting/ Copying Objects Archiving Copyright © 2012 by Yong-Gu Lee

Similar presentations


Presentation on theme: "Memory Management and Automatic Reference Counting/ Copying Objects Archiving Copyright © 2012 by Yong-Gu Lee"— Presentation transcript:

1 Memory Management and Automatic Reference Counting/ Copying Objects Archiving Copyright © 2012 by Yong-Gu Lee (lygu@gist.ac.kr)

2 Memory leaking NSObject* objA= [[NSObject alloc]init]; NSObject* objB= [[NSObject alloc]init]; objA = objB;//contents of objA is lost! Up until the release of Xcode 4.2 memory management was the subject of great consternation, one that required a great deal of understanding and forethought on the part of the programmer. Programmer had to carefully navigate the world of reference counts, retains, releases, and autoreleases in order to produce applications that judiciously used memory and weren’t subject to crashing at the most inopportune times, often as the result of trying to reference an object that had inadvertently been destroyed before its time had really passed. With the release of feature known as Automatic Reference Counting, or ARC, Xcode 4.2 programmers no longer have to think about memory management issues.

3 Automatic Reference Counting ARC Under the hood, reference counts are being maintained and tracked. System determines when to retain an object and when to relese it. Strong variables – By default, all object pointer variables are strong variables. That means that assigning an object reference to such a variable causes that object to be automatically retained. Further, the old object reference will be released before the assignment is made. Finally, strong variales are initialized to zero by default. And that’s true whether it’s an instance variable or a local or global variable.

4 objA = objB;// contents of objA is lost! If objA and objB are strong variables, the previous assignment actually works like this: [objB retain]; // retain new value [ objA release];/ / release the old value objA = objB; Because all object variables are strong variables by default, you don’t need to declare them as such. However you can explicitly do so by using the __strong keyword for a variable; __strong NSObject objA;

5 It’s important to note that properties are not strong by default. Their default attribute is unsafe_unretained (or, equivantly assign ). You’ve seen how to declare the strong attribute for a property. @ property ( strong, nonatomic) NSMutableArray *birdNames; The compiler makes sure that strong properties survive the event loop by retaining them on assignment. No such action is taken for properties that are unsafe_unreained (aka assign ) or weak.

6 If you need to hold onto an object to make sure it doesn’t get destroyed by someone else, you should retain it. Make sure to release the object when you are done with it

7 Retain cycle subview (child view) view (parent view) retain cycle When two objects have strong references to each other, you create what’s known as a retain cycle. The system will not destroy an object if there’s still a reference to it. So if two objects have strong references to each other, neither can ever be destroyed.

8 Weak variable subview (child view) view (parent view) A weak variable does not prevent deallocation of the object it references. When you declare a weak variable a few things happen; the system tracks the reference that is made on assignment to that variable. And when that referenced object gets deallocated, the weak variable gets automatically set to nil. That prevents any crashes that might occur by inadvertently sending a message to that variable weak strong

9 To declare a weak variable you use the __weak keyword: __weak UIView *parentView; or you use the weak attributes for a property @property (weak, nonatomic) UIView *parentView; Weak variables are also useful when working with delegates. By making the variable that holds the reference to the delegate a weak variable, you’re assured that the variable will be zeroed if the delegate object gets deallocated. Again, this can prevent the kind of system crashes that have caused headaches for many programmer prior to the invention of ARC. Note that weak variables are not supported in iOS 4. In such cases, you can still use the unsafe_unretained(or assign) property attriute or declare your variable to be __unsafe_unretained. However, realize that these variables are not zeroed automatically when the referenced object is deallocated.

10 @autoreleasepool Blocks You’ve seen in every example in this book so far how the compiler generates an @autoreleasepool directive inside your main routine. This directive encloses a block of statements that define an autoreleasepool context. Any objects created in that context that are autoreleased (and this is done automatically with ARC) will be destroyed by default at the end of that autoreleasepool block.

11 Copying objects

12 Shallow Versus Deep Copying NSMutableArray *dataArray = [NSMutableArray arrayWithObjects: [NSMutableString stringWithString: @"one"], [NSMutableString stringWithString: @"two"], [NSMutableString stringWithString: @"three"], nil ]; NSMutableArray *dataArray2; NSMutableString *mStr; NSLog (@"dataArray: "); for ( NSString *elem in dataArray ) NSLog (@" %@", elem); // make a copy, then change one of the strings dataArray2 = [dataArray mutableCopy]; mStr = [dataArray objectAtIndex: 0]; [mStr appendString: @"ONE"]; NSLog (@"dataArray: "); for ( NSString *elem in dataArray ) NSLog (@" %@", elem); NSLog (@"dataArray2: "); for ( NSString *elem in dataArray2 ) NSLog (@" %@", elem); dataArray: oneONE two three dataArray: one two three dataArray2: oneONE two three To make distinct copies of each element of the array, you must perform a deep copy. This means making copies of the contents of each object in the array, not just copies of the references to the objects (and think about what that implies if an element of an array is itself an array object). But deep copies are not performed by default when you use the copy or mutableCopy methods with the Foundation classes. In Chapter 19, “Archiving,” we show you how to use the Foundation’s archiving capabilities to create a deep copy of an object.

13 Implementing the Protocol We now show how you can add a copy method to your Fraction class. Recall that your Fraction class contains two integer instance variables, called numerator and denominator. To make a copy of one of these objects, you must allocate space for a new fraction and then simply copy the values of the two integers into the new fraction. When you implement the protocol, your class must implement the copyWithZone: method to respond to a copy message. (The copy message just sends a copyWithZone: message to your class with an argument of nil.) If you want to make a distinction between mutable and immutable copies, as we noted, you’ll also need to implement the mutableCopyWithZone: method according to the protocol. If you implement both methods, copyWithZone: should return an immutable copy and mutableCopyWithZone: should return a mutable one. Making a mutable copy of an object does not require that the object being copied also be mutable (and vice versa); it’s perfectly reasonable to want to make a mutable copy of an immutable object (consider a string object, for example).

14 Here’s what the @interface directive should look like: @interface Fraction: NSObject Fraction is a subclass of NSObject and conforms to the NSCopying protocol. In the implementation file Fraction.m, add the following definition for your new method: -(id) copyWithZone: (NSZone *) zone { Fraction *newFract = [[Fraction allocWithZone: zone] init]; [newFract setTo: numerator over: denominator]; return newFract; } The zone argument has to do with different memory zones that you can allocate and work with in your program. You need to deal with these only if you’re writing applications that allocate a lot of memory and you want to optimize the allocation by grouping them into these zones. You can take the value passed to copyWithZone: and hand it off to a memory allocation method called allocWithZone:.This method allocates memory in a specified zone. After allocating a new Fraction object, you copy the receiver’s numerator and denominator variables into it.The copyWithZone: method is supposed to return the new copy of the object, which you do in your method.

15 Archiving In Objective-C terms, archiving is the process of saving one or more objects in a format so that they can later be restored. Often this involves writing the object(s) to a file so it can subsequently be read back in. We discuss two methods for archiving data in this chapter: property lists and key-valued coding.

16 Archiving with XML Property Lists Mac OS X applications use XML propertylists (or plists) for storing things such as your default preferences, application settings, and configuration information, so it’s useful to know how to create them and read them back in. Their use for archiving purposes, however, is limited because when creating a property list for a data structure, specific object classes are not retained, multiple references to the same object are not stored, and the mutability of an object is not preserved.

17 If your objects are of type NSString, NSDictionary, NSArray, NSDate, NSData, or NSNumber, you can use the writeToFile:atomically: method implemented in these classes to write your data to a file. In the case of writing out a dictionary or an array, this method writes the data to the file in the format of an XML property list.

18 NSDictionary *glossary = [NSDictionary dictionaryWithObjectsAndKeys: @"ObjA", @"KeyofObjA", @"ObjB", @"KeyofObjB", nil ]; if ([glossary writeToFile: @"glossary" atomically: YES] == NO) NSLog (@"Save to file failed!"); The writeToFile:atomically: message is sent to your dictionary object glossary, causing the dictionary to be written to the file glossary in the form of a property list. The atomically parameter is set to YES, meaning that you want the write operation to be done to a temporary backup file first; once successful, the final data is to be moved to the specified file named glossary. This is a safeguard that protects the file from becoming corrupt if, for example, the system crashes in the middle of the write operation. In that case, the original glossary file (if it previously existed) isn’t harmed.

19 http://www.apple.com/DTDs/PropertyList-1.0.dtd"> KeyofObjA ObjA KeyofObjB ObjB You can see from the XML file that was created that the dictionary is written to the file as a set of key (... ) value (... ) pairs. When you create a property list from a dictionary, the keys in the dictionary must all be NSString objects. The elements of an array or the values in a dictionary can be NSString, NSArray, NSDictionary, NSData, NSDate, or NSNumber objects. To read an XML property list from a file into your program, you use the dictionaryWithContentsOfFile: or arrayWithContentsOfFile: methods. To read back data, use the dataWithContentsOfFile: method; to read back string objects, use the stringWithContentsOfFile: method.

20 NSDictionary *glossary; glossary = [NSDictionary dictionaryWithContentsOfFile: @"glossary"]; for ( NSString *key in glossary ) NSLog (@"%@: %@", key, [glossary objectForKey: key]); Above reads back the glossary written and then displays its contents. Your property lists don’t need to be created from an Objective-C program; the property list can come from any source. You can make your own property lists using a simple text editor, or you can use the Property List Editor program located in the /Developer/Applications/Utilities directory on Mac OS X systems. If you plan to work with property lists in your applications, you may want to take a look at the NSPropertyListSerialization class, which allows property lists to be written to and read from files in a machine-portable manner.

21 Archiving with NSKeyedArchiver A more flexible approach enables you to save any type of objects to a file, not just strings, arrays, and dictionaries. This is done by creating a keyed archive using the NSKeyedArchiver class.

22 Mac OX X has supported keyed archives since version 10.2. Before that, sequential archives were created with the NSArchiver class. Sequential archives require that the data in the archive be read back in precisely the same order in which it was written. A keyed archive is one in which each field of the archive has a name. When you archive an object, you give it a name, or key. When you retrieve it from the archive, you retrieve it by the same key. In that manner, objects can be written to the archive and re- trieved in any order. Furthermore, if new instance variables are added or removed to a class, your program can account for it.

23 NSDictionary *glossary = [NSDictionary dictionaryWithObjectsAndKeys: @"ObjA", @"KeyofObjA", @"ObjB", @"KeyofObjB", nil ]; [NSKeyedArchiver archiveRootObject: glossary toFile: @"glossary.archive"]; NSDictionary *glossary; glossary = [NSKeyedUnarchiver unarchiveObjectWithFile: @"glossary.archive"]; for ( NSString *key in glossary ) NSLog (@"%@: %@", key, [glossary objectForKey: key]); Writing Reading

24 Writing Encoding and Decoding Methods The encodeWithCoder: method is invoked each time the archiver wants to encode an object from the specified class, and the method tells it how to do so. In a similar manner, the initWithCoder: method is invoked each time an object from the specified class is to be decoded. In general, the encoder method should specify how to archive each instance variable in the object you want to save. Luckily, you have help doing this. For the basic Objective- C classes described previously, you can use the encodeObject:forKey: method. For basic underlying C data types (such as integers and floats), you use one of the methods listed in Table 19.1. The decoder method, initWithCoder:, works in reverse: You use decodeObject:forKey: to decode basic Objective-C classes and the appropriate decoder method shown in Table 19.1 for the basic data types.

25 @interface AddressCard : NSObject @property (copy, nonatomic) NSString *name, *email; -(void) setName: (NSString *) theName andEmail: (NSString *) theEmail; -(void) print; -(NSComparisonResult) compareNames: (id) element; @end -(void) encodeWithCoder: (NSCoder *) encoder { [encoder encodeObject: name forKey: @"AddressCardName"]; [encoder encodeObject: email forKey: @"AddressCardEmail"]; } -(id) initWithCoder: (NSCoder *) decoder { name = [decoder decodeObjectForKey: @"AddressCardName"]; email = [decoder decodeObjectForKey:@"AddressCardName"]; return self; }

26 -(void) encodeWithCoder: (NSCoder *) encoder { [encoder encodeObject: bookName forKey: @"AddressBookBookName"]; [encoder encodeObject: book forKey: @"AddressBookBook"]; } -(id) initWithCoder: (NSCoder *) decoder { bookName = [decoder decodeObjectForKey: @"AddressBookBookName"]; book = [decoder decodeObjectForKey: @"AddressBookBook"]; return self; } @interface AddressBook : NSObject @property (nonatomic, copy) NSString *bookName; @property (nonatomic, strong) NSMutableArray *book; … @end

27 if ([NSKeyedArchiver archiveRootObject: myBook toFile: @"addrbook.arch"] == NO) NSLog (@"archiving failed"); AddressBook *myBookFromArchive; myBookFromArchive = [NSKeyedUnarchiver unarchiveObjectWithFile: @"addrbook.arch"]; [myBookFromArchive list]; Write and read

28 @interface Foo: NSObject { NSString *strVal; int intVal; float floatVal; } @property (copy, nonatomic) NSString *strVal; @property int intVal; @property float floatVal; @end @implementation Foo @synthesize strVal, intVal, floatVal; -(void) encodeWithCoder: (NSCoder *) encoder { [encoder encodeObject: strVal forKey: @"FoostrVal"]; [encoder encodeInt: intVal forKey: @"FoointVal"]; [encoder encodeFloat: floatVal forKey: @"FoofloatVal"]; } -(id) initWithCoder: (NSCoder *) decoder { strVal = [decoder decodeObjectForKey: @"FoostrVal"]; intVal = [decoder decodeIntForKey: @"FoointVal"]; floatVal = [decoder decodeFloatForKey: @"FoofloatVal"]; return self; } @end

29 Foo *myFoo1 = [[Foo alloc] init]; Foo *myFoo2; [myFoo1 setStrVal: @"This is the string"]; [myFoo1 setIntVal: 12345]; [myFoo1 setFloatVal: 98.6]; [NSKeyedArchiver archiveRootObject: myFoo1 toFile: @"foo.arch"]; myFoo2 = [NSKeyedUnarchiver unarchiveObjectWithFile: @"foo.arch"]; NSLog (@"%@\n%i\n%g", [myFoo2 strVal], [myFoo2 intVal],[myFoo2 floatVal]); This is the string 12345 98.6

30 Using NSData to Create Custom Archives NSData object can be used to reserve an area of memory into which you can store data. Typical uses of this data area might be to provide temporary storage for data that will subsequently be written to a file or perhaps to hold the contents of a file read from the disk. The simplest way to create a mutable data area is with the data method: dataArea = [NSMutableData data]; This creates an empty buffer space whose size expands as needed as the program executes. As a simple example, let’s assume that you want to archive your address book and one of your Foo objects in the same file. Assume for this example that you’ve added keyed archiving methods to the AddressBook and AddressCard classes

31 Foo *myFoo1 = [[Foo alloc] init]; NSMutableData *dataArea; NSKeyedArchiver *archiver; AddressBook *myBook2; [myFoo1 setStrVal:@"This is the string"]; [myFoo1 setIntVal: 12345]; [myFoo1 setFloatVal: 98.6]; // Set up a data area and connect it to an NSKeyedArchiver object dataArea = [NSMutableData data]; archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: dataArea]; // Now we can begin to archive objects [archiver encodeObject: myBook2 forKey: @"myaddrbook"]; [archiver encodeObject: myFoo1 forKey: @"myfoo1"]; [archiver finishEncoding]; // Write the archived data area to a file if ([dataArea writeToFile: @"myArchive" atomically: YES] == NO) NSLog (@"Archiving failed!"); specify the area in which to write the archived data

32 NSData *dataArea3; NSKeyedUnarchiver *unarchiver; Foo *myFoo3; AddressBook *myBook3; // Read in the archive and connect an // NSKeyedUnarchiver object to it dataArea3 = [NSData dataWithContentsOfFile: @"myArchive"]; if (! dataArea3) { NSLog (@"Can’t read back archive file!"); return 1; } unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData: dataArea3]; // Decode the objects we previously stored in the archive myBook3 = [unarchiver decodeObjectForKey: @"myaddrbook"]; myFoo3 = [unarchiver decodeObjectForKey: @"myfoo1"]; [unarchiver finishDecoding]; // Verify that the restore was successful [myBook list]; NSLog (@"%@\n%i\n%g", [myFoo1 strVal], [myFoo1 intVal], [myFoo1 floatVal]);

33 Using the Archiver to Copy Objects NSData *data; NSMutableArray *dataArray = [NSMutableArray arrayWithObjects: [NSMutableString stringWithString: @"one"], [NSMutableString stringWithString: @"two"], [NSMutableString stringWithString: @"three"], nil ]; NSMutableArray *dataArray2; NSMutableString *mStr; // Make a deep copy using the archiver data = [NSKeyedArchiver archivedDataWithRootObject: dataArray]; dataArray2 = [NSKeyedUnarchiver unarchiveObjectWithData: data]; mStr = [dataArray2 objectAtIndex: 0]; [mStr appendString: @"ONE"]; NSLog (@"dataArray: "); for ( NSString *elem in dataArray ) NSLog (@"%@", elem); NSLog (@"\ndataArray2: "); for ( NSString *elem in dataArray2 ) NSLog (@"%@", elem);


Download ppt "Memory Management and Automatic Reference Counting/ Copying Objects Archiving Copyright © 2012 by Yong-Gu Lee"

Similar presentations


Ads by Google