├── README.md ├── chapter_1 ├── item_1.txt ├── item_2.txt ├── item_3.txt ├── item_4.txt └── item_5.txt ├── chapter_2 ├── item_10.txt ├── item_11.txt ├── item_12.txt ├── item_13.txt ├── item_14.txt ├── item_6.txt ├── item_7.txt ├── item_8.txt └── item_9.txt ├── chapter_3 ├── item_15.txt ├── item_16.txt ├── item_17.txt ├── item_18.txt ├── item_19.txt ├── item_20.txt ├── item_21.txt └── item_22.txt ├── chapter_4 ├── item_23.txt ├── item_24.txt ├── item_25.txt ├── item_26.txt ├── item_27.txt └── item_28.txt ├── chapter_5 ├── item_29.txt ├── item_30.txt ├── item_31.txt ├── item_32.txt ├── item_33.txt ├── item_34.txt ├── item_35.txt ├── item_36.txt └── item_37.txt ├── chapter_6 ├── item_38.txt ├── item_39.txt ├── item_40.txt ├── item_41.txt ├── item_42.txt ├── item_43.txt ├── item_44.txt ├── item_45.txt └── item_46.txt └── chapter_7 ├── item_47.txt ├── item_48.txt ├── item_49.txt ├── item_50.txt ├── item_51.txt └── item_52.txt /README.md: -------------------------------------------------------------------------------- 1 | Effective Objective-C 2.0 2 | ========================= 3 | 4 | This is a repo containing all the code that appears in Effective Objective-C 2.0 ([Amazon](http://bit.ly/EffObjCAUS), [InformIT](http://bit.ly/EffObjC3)). Also, be sure to check-out the [main site](http://www.effectiveobjectivec.com/) for the book. 5 | -------------------------------------------------------------------------------- /chapter_1/item_1.txt: -------------------------------------------------------------------------------- 1 | // Item 1 2 | -------------------------------------------------------------------------------- /chapter_1/item_2.txt: -------------------------------------------------------------------------------- 1 | // Item 2 2 | 3 | // Simple EOCPerson class 4 | // EOCPerson.h 5 | #import 6 | 7 | @interface EOCPerson : NSObject 8 | @property (nonatomic, copy) NSString *firstName; 9 | @property (nonatomic, copy) NSString *lastName; 10 | @end 11 | 12 | // EOCPerson.m 13 | #import "EOCPerson.h" 14 | 15 | @implementation EOCPerson 16 | // Implementation of methods 17 | @end 18 | 19 | 20 | // Requiring an import 21 | // EOCPerson.h 22 | #import 23 | 24 | @interface EOCPerson : NSObject 25 | @property (nonatomic, copy) NSString *firstName; 26 | @property (nonatomic, copy) NSString *lastName; 27 | @property (nonatomic, strong) EOCEmployer *employer; 28 | @end 29 | 30 | 31 | // Forward declaring 32 | // EOCPerson.h 33 | #import 34 | 35 | @class EOCEmployer; 36 | 37 | @interface EOCPerson : NSObject 38 | @property (nonatomic, copy) NSString *firstName; 39 | @property (nonatomic, copy) NSString *lastName; 40 | @property (nonatomic, strong) EOCEmployer *employer; 41 | @end 42 | 43 | // EOCPerson.m 44 | #import "EOCPerson.h" 45 | #import "EOCEmployer.h" 46 | 47 | @implementation EOCPerson 48 | // Implementation of methods 49 | @end 50 | 51 | 52 | // Importing a header with a protocol in it 53 | // EOCRectangle.h 54 | #import "EOCShape.h" 55 | #import "EOCDrawable.h" 56 | 57 | @interface EOCRectangle : EOCShape 58 | @property (nonatomic, assign) float width; 59 | @property (nonatomic, assign) float height; 60 | @end 61 | -------------------------------------------------------------------------------- /chapter_1/item_3.txt: -------------------------------------------------------------------------------- 1 | // Item 3 2 | 3 | NSString *someString = @"Effective Objective-C 2.0"; 4 | 5 | NSNumber *someNumber = [NSNumber numberWithInt:1]; 6 | NSNumber *someNumber = @1; 7 | 8 | NSNumber *intNumber = @1; 9 | NSNumber *floatNumber = @2.5f; 10 | NSNumber *doubleNumber = @3.14159; 11 | NSNumber *boolNumber = @YES; 12 | NSNumber *charNumber = @‘a’; 13 | 14 | int x = 5; 15 | float y = 6.32f; 16 | NSNumber *expressionNumber = @(x * y); 17 | 18 | NSArray *animals = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil]; 19 | NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"]; 20 | 21 | NSString *dog = [animals objectAtIndex:1]; 22 | NSString *dog = animals[1]; 23 | 24 | id object1 = /* … /*; 25 | id object2 = /* … /*; 26 | id object3 = /* … /*; 27 | 28 | NSArray *arrayA = [NSArray arrayWithObjects:object1, object2, object3, nil]; 29 | NSArray *arrayB = @[object1, object2, object3]; 30 | 31 | NSDictionary *personData = 32 | [NSDictionary dictionaryWithObjectsAndKeys: 33 | @"Matt", @"firstName", 34 | @"Galloway", @"lastName", 35 | [NSNumber numberWithInt:28], @"age", 36 | nil]; 37 | NSDictionary *personData = 38 | @{@"firstName" : @"Matt", 39 | @"lastName" : @"Galloway", 40 | @"age" : @28}; 41 | 42 | NSString *lastName = [personData objectForKey:@"lastName"]; 43 | NSString *lastName = personData[@"lastName"]; 44 | 45 | [mutableArray replaceObjectAtIndex:1 withObject:@"dog"]; 46 | [mutableDictionary setObject:@"Galloway" forKey:@"lastName"]; 47 | mutableArray[1] = @"dog"; 48 | mutableDictionary[@"lastName"] = @"Galloway"; 49 | 50 | NSMutableArray *mutable = [@[@1, @2, @3, @4, @5] mutableCopy]; 51 | -------------------------------------------------------------------------------- /chapter_1/item_4.txt: -------------------------------------------------------------------------------- 1 | // Item 4 2 | 3 | // EOCAnimatedView.h 4 | #import 5 | 6 | @interface EOCAnimatedView : UIView 7 | - (void)animate; 8 | @end 9 | 10 | // EOCAnimatedView.m 11 | #import "EOCAnimatedView.h" 12 | 13 | static const NSTimeInterval kAnimationDuration = 0.3; 14 | 15 | @implementation EOCAnimatedView 16 | - (void)animate { 17 | [UIView animateWithDuration:kAnimationDuration 18 | animations:^(){ 19 | // Perform animations 20 | }]; 21 | } 22 | @end 23 | 24 | 25 | // In the header file 26 | extern NSString *const StringConstant; 27 | 28 | // In the implementation file 29 | NSString *const StringConstant = @"VALUE"; 30 | 31 | 32 | // EOCLoginManager.h 33 | #import 34 | 35 | extern NSString *const EOCLoginManagerDidLoginNotification; 36 | 37 | @interface EOCLoginManager : NSObject 38 | - (void)login; 39 | @end 40 | 41 | // EOCLoginManager.m 42 | #import "EOCLoginManager.h" 43 | 44 | NSString *const EOCLoginManagerDidLoginNotification = @"EOCLoginManagerDidLoginNotification"; 45 | 46 | @implementation EOCLoginManager 47 | 48 | - (void)login { 49 | // Perform login asynchronously, then call `p_didLogin`. 50 | } 51 | 52 | - (void)p_didLogin { 53 | [[NSNotificationCenter defaultCenter] 54 | postNotificationName:EOCLoginManagerDidLoginNotification 55 | object:nil]; 56 | } 57 | 58 | @end 59 | 60 | 61 | // EOCAnimatedView.h 62 | extern const NSTimeInterval EOCAnimatedViewAnimationDuration; 63 | 64 | // EOCAnimatedView.m 65 | const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3; 66 | -------------------------------------------------------------------------------- /chapter_1/item_5.txt: -------------------------------------------------------------------------------- 1 | // Item 5 2 | 3 | enum EOCConnectionState { 4 | EOCConnectionStateDisconnected, 5 | EOCConnectionStateConnecting, 6 | EOCConnectionStateConnected, 7 | }; 8 | 9 | enum EOCConnectionState state = EOCConnectionStateNotConnected; 10 | 11 | enum EOCConnectionState { 12 | EOCConnectionStateDisconnected, 13 | EOCConnectionStateConnecting, 14 | EOCConnectionStateConnected, 15 | }; 16 | typedef enum EOCConnectionState EOCConnectionState; 17 | 18 | EOCConnectionState state = EOCConnectionStateNotConnected; 19 | 20 | enum EOCConnectionStateConnectionState : NSInteger { … }; 21 | 22 | enum EOCConnectionStateConnectionState : NSInteger; 23 | 24 | enum EOCConnectionStateConnectionState { 25 | EOCConnectionStateDisconnected = 1, 26 | EOCConnectionStateConnecting, 27 | EOCConnectionStateConnected, 28 | }; 29 | 30 | enum EOCMethodOptions { 31 | EOCMethodOptionOne = 1 << 0, 32 | EOCMethodOptionTwo = 1 << 1, 33 | EOCMethodOptionThree = 1 << 2, 34 | }; 35 | 36 | enum EOCMethodOptions options = EOCMethodOptionOne | EOCMethodOptionThree; 37 | if (options & OptionOne) { 38 | // OptionOne is set 39 | } 40 | 41 | typedef NS_ENUM(NSUInteger, EOCConnectionState) { 42 | EOCConnectionStateDisconnected, 43 | EOCConnectionStateConnecting, 44 | EOCConnectionStateConnected, 45 | }; 46 | typedef NS_OPTIONS(NSUInteger, EOCMethodOptions) { 47 | EOCMethodOptionOne = 1 << 0, 48 | EOCMethodOptionTwo = 1 << 1, 49 | EOCMethodOptionThree = 1 << 2, 50 | }; 51 | 52 | typedef enum EOCConnectionState : NSUInteger EOCConnectionState; 53 | enum EOCConnectionState : NSUInteger { 54 | EOCConnectionStateDisconnected, 55 | EOCConnectionStateConnecting, 56 | EOCConnectionStateConnected, 57 | }; 58 | 59 | typedef enum EOCMethodOptions : int EOCMethodOptions; 60 | enum EOCMethodOptions : int { 61 | EOCMethodOptionOne = 1 << 0, 62 | EOCMethodOptionTwo = 1 << 1, 63 | EOCMethodOptionThree = 1 << 2, 64 | }; 65 | 66 | EOCMethodOptions options = EOCMethodOptionOne | EOCMethodOptionTwo; 67 | 68 | typedef NS_ENUM(NSUInteger, EOCConnectionState) { 69 | EOCConnectionStateDisconnected, 70 | EOCConnectionStateConnecting, 71 | EOCConnectionStateConnected, 72 | }; 73 | 74 | // … 75 | 76 | switch (_currentState) { 77 | case EOCConnectionStateDisconnected: 78 | // Handle disconnected state 79 | break; 80 | case EOCConnectionStateConnecting: 81 | // Handle connecting state 82 | break; 83 | case EOCConnectionStateConnected: 84 | // Handle connected state 85 | break; 86 | } 87 | -------------------------------------------------------------------------------- /chapter_2/item_10.txt: -------------------------------------------------------------------------------- 1 | // Item 10 2 | 3 | // C early binding 4 | #import 5 | 6 | void printHello() { 7 | printf("Hello, world!\n"); 8 | } 9 | void printGoodbye() { 10 | printf("Goodbye, world!\n"); 11 | } 12 | 13 | void doTheThing(int type) { 14 | if (type == 0) { 15 | printHello(); 16 | } else { 17 | printGoodbye(); 18 | } 19 | return 0; 20 | } 21 | 22 | 23 | // C late binding 24 | #import 25 | 26 | void printHello() { 27 | printf("Hello, world!\n"); 28 | } 29 | void printGoodbye() { 30 | printf("Goodbye, world!\n"); 31 | } 32 | 33 | void doTheThing(int type) { 34 | void (*fnc)(); 35 | if (type == 0) { 36 | fnc = printHello; 37 | } else { 38 | fnc = printGoodbye; 39 | } 40 | fnc(); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /chapter_2/item_11.txt: -------------------------------------------------------------------------------- 1 | // Item 11 2 | 3 | // `resolveInstanceMethod' example 4 | id autoDictionaryGetter(id self, SEL _cmd); 5 | void autoDictionarySetter(id self, SEL _cmd, id value); 6 | 7 | + (BOOL)resolveInstanceMethod:(SEL)selector { 8 | NSString *selectorString = NSStringFromSelector(selector); 9 | if (/* selector is from a @dynamic property */) { 10 | if ([selectorString hasPrefix:@"set"]) { 11 | class_addMethod(self, selector, (IMP)autoDictionarySetter, "v@:@"); 12 | } else { 13 | class_addMethod(self, selector, (IMP)autoDictionaryGetter, "@@:"); 14 | } 15 | return YES; 16 | } 17 | return [super resolveInstanceMethod:selector]; 18 | } 19 | 20 | 21 | // Full "AutoDictionary" example 22 | #import 23 | 24 | @interface AutoDictionary : NSObject 25 | @property (nonatomic, strong) NSString *string; 26 | @property (nonatomic, strong) NSNumber *number; 27 | @property (nonatomic, strong) NSDate *date; 28 | @property (nonatomic, strong) id opaqueObject; 29 | @end 30 | 31 | #import "AutoDictionary.h" 32 | #import 33 | 34 | id autoDictionaryGetter(id self, SEL _cmd) { 35 | // Get the backing store from the object 36 | AutoDictionary *typedSelf = (AutoDictionary*)self; 37 | NSMutableDictionary *backingStore = typedSelf.backingStore; 38 | 39 | // The key is simply the selector name 40 | NSString *key = NSStringFromSelector(_cmd); 41 | 42 | // Return the value 43 | return [backingStore objectForKey:key]; 44 | } 45 | 46 | void autoDictionarySetter(id self, SEL _cmd, id value) { 47 | // Get the backing store from the object 48 | AutoDictionary *typedSelf = (AutoDictionary*)self; 49 | NSMutableDictionary *backingStore = typedSelf.backingStore; 50 | 51 | /** The selector will be for example, "setOpaqueObject:". 52 | * We need to remove the "set", ":" and lowercase the first 53 | * letter of the remainder. 54 | */ 55 | NSString *selectorString = NSStringFromSelector(_cmd); 56 | NSMutableString *key = [selectorString mutableCopy]; 57 | 58 | // Remove the `:' at the end 59 | [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)]; 60 | 61 | // Remove the `set' prefix 62 | [key deleteCharactersInRange:NSMakeRange(0, 3)]; 63 | 64 | // Lowercase the first character 65 | NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString]; 66 | [key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar]; 67 | 68 | if (value) { 69 | [backingStore setObject:value forKey:key]; 70 | } else { 71 | [backingStore removeObjectForKey:key]; 72 | } 73 | } 74 | 75 | @interface AutoDictionary () 76 | @property (nonatomic, strong) NSMutableDictionary *backingStore; 77 | @end 78 | 79 | @implementation AutoDictionary 80 | @dynamic string, number, date, opaqueObject; 81 | 82 | - (id)init { 83 | if ((self = [super init])) { 84 | _backingStore = [NSMutableDictionary new]; 85 | } 86 | return self; 87 | } 88 | 89 | + (BOOL)resolveInstanceMethod:(SEL)selector { 90 | NSString *selectorString = NSStringFromSelector(selector); 91 | if ([selectorString hasPrefix:@"set"]) { 92 | class_addMethod(self, selector, (IMP)autoDictionarySetter, "v@:@"); 93 | } else { 94 | class_addMethod(self, selector, (IMP)autoDictionaryGetter, "@@:"); 95 | } 96 | return YES; 97 | } 98 | 99 | @end 100 | 101 | 102 | // Using AutoDictionary 103 | AutoDictionary *dict = [AutoDictionary new]; 104 | dict.date = [NSDate dateWithTimeIntervalSince1970:475372800]; 105 | NSLog(@"dict.date = %@", dict.date); 106 | // Output: dict.date = 1985-01-24 00:00:00 +0000 107 | -------------------------------------------------------------------------------- /chapter_2/item_12.txt: -------------------------------------------------------------------------------- 1 | // Item 12 2 | 3 | // Exchanging methods 4 | Method originalMethod = 5 | class_getInstanceMethod([NSString class], 6 | @selector(lowercaseString)); 7 | Method swappedMethod = 8 | class_getInstanceMethod([NSString class], 9 | @selector(uppercaseString)); 10 | method_exchangeImplementations(originalMethod, swappedMethod); 11 | 12 | 13 | // Using the method exchanging of NSString 14 | NSString *string = @"ThIs iS tHe StRiNg"; 15 | 16 | NSString *lowercaseString = [string lowercaseString]; 17 | NSLog(@"lowercaseString = %@", lowercaseString); 18 | // Output: lowercaseString = THIS IS THE STRING 19 | 20 | NSString *uppercaseString = [string uppercaseString]; 21 | NSLog(@"uppercaseString = %@", uppercaseString); 22 | // Output: uppercaseString = this is the string 23 | 24 | 25 | // Using a category and then swizzling methods 26 | @interface NSString (MyAdditions) 27 | - (NSString*)myLowercaseString; 28 | @end 29 | 30 | @implementation NSString (MyAdditions) 31 | - (NSString*)myLowercaseString { 32 | NSString *lowercase = [self myLowercaseString]; 33 | NSLog(@"Lowercasing string: %@ => %@", self, lowercase); 34 | return lowercase; 35 | } 36 | @end 37 | 38 | Method originalMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString)); 39 | Method swappedMethod = class_getInstanceMethod([NSString class], @selector(myLowercaseString)); 40 | method_exchangeImplementations(originalMethod, swappedMethod); 41 | 42 | NSString *string = @"ThIs iS tHe StRiNg"; 43 | NSString *lowercaseString = [string lowercaseString]; 44 | // Output: Lowercasing string: ThIs iS tHe StRiNg => this is the string 45 | -------------------------------------------------------------------------------- /chapter_2/item_13.txt: -------------------------------------------------------------------------------- 1 | // Item 13 2 | 3 | // Button delegate example 4 | @class Button; 5 | @protocol ButtonDelegate 6 | @optional 7 | - (void)buttonWasClicked:(Button*)button; 8 | - (void)buttonWasDoubleClicked:(Button*)button; 9 | @end 10 | 11 | @interface Button : NSObject 12 | @property (nonatomic, weak) id delegate; 13 | @end 14 | 15 | @implementation Button 16 | ... 17 | - (void)buttonWasClicked { 18 | if ([_delegate respondsToSelector:@selector(buttonWasClicked:)]) { 19 | [_delegate buttonWasClicked:self]; 20 | } 21 | // Do button related things, e.g. change highlight color 22 | } 23 | 24 | - (void)buttonWasDoubleClicked { 25 | if ([_delegate respondsToSelector:@selector(buttonWasDoubleClicked:)]) { 26 | [_delegate buttonWasDoubleClicked:self]; 27 | } 28 | // Do button related things, e.g. change highlight color 29 | } 30 | ... 31 | @end 32 | 33 | 34 | // Inspecting class hierarchy 35 | NSMutableDictionary *dict = [NSMutableDictionary new]; 36 | [dict isMemberOfClass:[NSDictionary class]]; ///< NO 37 | [dict isMemberOfClass:[NSMutableDictionary class]]; ///< YES 38 | [dict isKindOfClass:[NSDictionary class]]; ///< YES 39 | [dict isKindOfClass:[NSArray class]]; ///< NO 40 | 41 | 42 | // Example of using inspection of types 43 | - (NSString*)commaSeparatedStringFromObjects:(NSArray*)array { 44 | NSMutableString *string = [NSMutableString new]; 45 | for (id object in array) { 46 | if ([object isKindOfClass:[NSString class]]) { 47 | [string appendFormat:@"%@,", object]; 48 | } else if ([object isKindOfClass:[NSNumber class]]) { 49 | [string appendFormat:@"%d,", [object intValue]]; 50 | } else if ([object isKindOfClass:[NSData class]]) { 51 | NSString *base64Encoded = /* base64 encode the data */; 52 | [string appendFormat:@"%@,", base64Encoded]; 53 | } else { 54 | // Type not supported 55 | } 56 | } 57 | return string; 58 | } 59 | -------------------------------------------------------------------------------- /chapter_2/item_14.txt: -------------------------------------------------------------------------------- 1 | // Item 14 2 | 3 | // Class hierarchy checking 4 | NSMutableDictionary *dict = [NSMutableDictionary new]; 5 | [dict isMemberOfClass:[NSDictionary class]]; ///< NO 6 | [dict isMemberOfClass:[NSMutableDictionary class]]; ///< YES 7 | [dict isKindOfClass:[NSDictionary class]]; ///< YES 8 | [dict isKindOfClass:[NSArray class]]; ///< NO 9 | 10 | 11 | // Inspecting type of objects in an array during serialisation 12 | - (NSString*)commaSeparatedStringFromObjects:(NSArray*)array { 13 | NSMutableString *string = [NSMutableString new]; 14 | for (id object in array) { 15 | if ([object isKindOfClass:[NSString class]]) { 16 | [string appendFormat:@"%@,", object]; 17 | } else if ([object isKindOfClass:[NSNumber class]]) { 18 | [string appendFormat:@"%d,", [object intValue]]; 19 | } else if ([object isKindOfClass:[NSData class]]) { 20 | NSString *base64Encoded = /* base64 encoded data */; 21 | [string appendFormat:@"%@,", base64Encoded]; 22 | } else { 23 | // Type not supported 24 | } 25 | } 26 | return string; 27 | } 28 | 29 | 30 | // Class equality 31 | id object = /* ... */; 32 | if ([object class] == [EOCSomeClass class]) { 33 | // 'object' is an instance of EOCSomeClass 34 | } -------------------------------------------------------------------------------- /chapter_2/item_6.txt: -------------------------------------------------------------------------------- 1 | // Item 6 2 | 3 | // Person interface 4 | @interface Person : NSObject { 5 | @public 6 | NSDate *_dateOfBirth; 7 | NSString *_firstName; 8 | NSString *_lastName; 9 | @private 10 | NSString *_someInternalData; 11 | } 12 | 13 | 14 | // Person interface with properties 15 | @interface Person : NSObject 16 | @property NSString *firstName; 17 | @property NSString *lastName; 18 | @end 19 | 20 | 21 | // Person interface what properties equate to 22 | @interface Person : NSObject 23 | - (NSString*)firstName; 24 | - (void)setFirstName:(NSString*)firstName; 25 | - (NSString*)lastName; 26 | - (void)setLastName:(NSString*) lastName; 27 | @end 28 | 29 | Person *aPerson = [Person new]; 30 | 31 | aPerson.firstName = @"Bob"; // Same as: 32 | [aPerson setFirstName:@"Bob"]; 33 | 34 | NSString *lastName = aPerson.lastName; // Same as: 35 | NSString *lastName = [aPerson lastName]; 36 | 37 | 38 | // Synthesizing properties 39 | @implementation Person 40 | @synthesize firstName = _myFirstName; 41 | @synthesize lastName = _myLastName; 42 | @end 43 | 44 | 45 | // NSManagedObject example for @dynamic 46 | @interface Person : NSManagedObject 47 | @property NSString *firstName; 48 | @property NSString *lastName; 49 | @end 50 | 51 | @implementation Person 52 | @dynamic firstName, lastName; 53 | @end 54 | 55 | -------------------------------------------------------------------------------- /chapter_2/item_7.txt: -------------------------------------------------------------------------------- 1 | // Item 7 2 | 3 | // Person interface 4 | @interface Person : NSObject 5 | @property (nonatomic, copy) NSString *firstName; 6 | @property (nonatomic, copy) NSString *lastName; 7 | @property (nonatomic, copy) NSString *fullName; // Convenience for firstName + " " + lastName 8 | @end 9 | 10 | 11 | // Implementing `fullName' accessors 12 | - (NSString*)fullName { 13 | return [NSString stringWithFormat:@"%@ %@", 14 | self.firstName, self.lastName]; 15 | } 16 | 17 | - (void)setFullName:(NSString*)fullName { 18 | NSArray *components = [fullName componentsSeparatedByString:@" "]; 19 | self.firstName = [components objectAtIndex:0]; 20 | self.lastName = [components objectAtIndex:1]; 21 | } 22 | 23 | 24 | // Implementing `fullName' accessors with direct access 25 | - (NSString*)fullName { 26 | return [NSString stringWithFormat:@"%@ %@", 27 | _firstName, _lastName]; 28 | } 29 | 30 | - (void)setFullName:(NSString*)fullName { 31 | NSArray *components = [fullName componentsSeparatedByString:@" "]; 32 | _firstName = [components objectAtIndex:0]; 33 | _lastName = [components objectAtIndex:1]; 34 | } 35 | 36 | 37 | // `lastName' setter throwing exception on invalid data 38 | - (void)setLastName:(NSString*)lastName { 39 | if (![lastName isEqualToString:@"Smith"]) { 40 | [NSException raise:NSInvalidArgumentException 41 | format:@"Last name must be Smith"]; 42 | } 43 | self.lastName = lastname; 44 | } 45 | 46 | 47 | // `brain' lazy initialisation 48 | - (Brain*)brain { 49 | if (!_brain) { 50 | _brain = [Brain new]; 51 | } 52 | return _brain; 53 | } 54 | -------------------------------------------------------------------------------- /chapter_2/item_8.txt: -------------------------------------------------------------------------------- 1 | // Item 7 2 | 3 | // Examples of equality and non-equality 4 | NSString *foo = @"Badger 123"; 5 | NSString *bar = [NSString stringWithFormat:@"Badger %i", 123]; 6 | BOOL equalA = (foo == bar); //< equalA = NO 7 | BOOL equalB = [foo isEqual:bar]; //< equalB = YES 8 | BOOL equalC = [foo isEqualToString:bar]; //< equalC = YES 9 | 10 | 11 | // Equality methods 12 | - (BOOL)isEqual:(id)object; 13 | - (NSUInteger)hash; 14 | 15 | 16 | // Person interface 17 | @interface Person : NSObject 18 | @property (nonatomic, copy) NSString *firstName; 19 | @property (nonatomic, copy) NSString *lastName; 20 | @property (nonatomic, assign) NSUInteger age; 21 | @end 22 | 23 | 24 | 25 | // Equality method 26 | - (BOOL)isEqual:(id)object { 27 | if (self == object) return YES; 28 | if ([self class] != [object class]) return NO; 29 | 30 | Person *otherPerson = (Person*)object; 31 | if (![_firstName isEqualToString:otherPerson.firstName]) 32 | return NO; 33 | if (![_lastName isEqualToString:otherPerson.lastName]) 34 | return NO; 35 | if (_age != otherPerson.age) 36 | return NO; 37 | return YES; 38 | } 39 | 40 | 41 | // Super simple hash method 42 | - (NSUInteger)hash { 43 | return 1337; 44 | } 45 | 46 | 47 | // Hash method hijacking NSString's hash method 48 | - (NSUInteger)hash { 49 | NSString *stringToHash = [NSString stringWithFormat:@"%@:%@:%i", _firstName, _lastName, _age]; 50 | return [stringToHash hash]; 51 | } 52 | 53 | 54 | // Hash method by building up from individual values 55 | - (NSUInteger)hash { 56 | NSUInteger firstNameHash = [_firstName hash]; 57 | NSUInteger lastNameHash = [_lastName hash]; 58 | NSUInteger ageHash = _age; 59 | return firstNameHash ^ lastNameHash ^ ageHash; 60 | } 61 | 62 | 63 | 64 | // Class specific equality methods 65 | - (BOOL)isEqualToPerson:(Person*)object { 66 | if (self == object) return YES; 67 | 68 | Person *otherPerson = (Person*)object; 69 | if (![_firstName isEqualToString:otherPerson.firstName]) 70 | return NO; 71 | if (![_lastName isEqualToString:otherPerson.lastName]) 72 | return NO; 73 | if (_age != otherPerson.age) 74 | return NO; 75 | return YES; 76 | } 77 | 78 | - (BOOL)isEqual:(id)object { 79 | if ([self class] == [object class]) { 80 | return [self isEqualToPerson:(Person*)object]; 81 | } else { 82 | return [super isEqual:object]; 83 | } 84 | } 85 | 86 | 87 | 88 | // Example of mutable objects in collection problem 89 | NSMutableSet *set = [NSMutableSet new]; 90 | 91 | NSMutableArray *arrayA = [@[@1, @2] mutableCopy]; 92 | [set addObject:arrayA]; 93 | NSLog(@"set = %@", set); 94 | // Output: set = {((1,2))} 95 | 96 | So the set contains 1 object - an array with 2 objects in it. Now add another array which contains equal objects in the same order, such that the array already in the set and the new one are equal: 97 | 98 | NSMutableArray *arrayB = [@[@1, @2] mutableCopy]; 99 | [set addObject:arrayB]; 100 | NSLog(@"set = %@", set); 101 | // Output: set = {((1,2))} 102 | 103 | We see that the set still contains just a single object, since the object added is equal to the object already in there. Now we add another array to the set, but this time one that is not equal to the array already in the set: 104 | 105 | NSMutableArray *arrayC = [@[@1] mutableCopy]; 106 | [set addObject:arrayC]; 107 | NSLog(@"set = %@", set); 108 | // Output: set = {((1),(1,2))} 109 | 110 | As expected, the set now contains two arrays. The original one and the new one, since arrayC does not equal the one already in the set. Finally we mutate arrayC to be equal to the other array already in the set: 111 | 112 | [arrayC addObject:@2]; 113 | NSLog(@"set = %@", set); 114 | // Output: set = {((1,2),(1,2))} 115 | 116 | Ah oh dear, there’s now two arrays in the set which are equal to each other! A set is not meant to allow this, but it has been unable to maintain its semantics because we’ve mutated one of the objects that was already in the set. What’s even more awkward is if the set is then copied: 117 | 118 | NSSet *setB = [set copy]; 119 | NSLog(@"setB = %@", setB); 120 | // Output: setB = {((1,2))} 121 | -------------------------------------------------------------------------------- /chapter_2/item_9.txt: -------------------------------------------------------------------------------- 1 | // Item 9 2 | 3 | // Class cluster example 4 | typedef NS_ENUM(NSUInteger, EmployeeType) { 5 | EmployeeTypeDeveloper, 6 | EmployeeTypeDesigner, 7 | EmployeeTypeFinance, 8 | }; 9 | 10 | @interface Employee : NSObject 11 | 12 | @property (copy) NSString *name; 13 | @property NSUInteger salary; 14 | 15 | // Helper for creating Employee objects 16 | + (Employee*)employeeWithType:(EmployeeType)type; 17 | 18 | // Make an Employee do their respective days work 19 | - (void)doADaysWork; 20 | 21 | @end 22 | 23 | @implementation Employee 24 | 25 | + (Employee*)employeeWithType:(EmployeeType)type { 26 | switch (type) { 27 | case EmployeeTypeDeveloper: 28 | return [EmployeeDeveloper new]; 29 | break; 30 | case EmployeeTypeDesigner: 31 | return [EmployeeDesigner new]; 32 | break; 33 | case EmployeeTypeFinance: 34 | return [EmployeeFinance new]; 35 | break; 36 | } 37 | } 38 | 39 | - (void)doADaysWork { 40 | // Subclasses implement this. 41 | } 42 | 43 | @end 44 | 45 | 46 | // Subclass of the class cluster 47 | @interface EmployeeDeveloper : Employee 48 | @end 49 | 50 | @implementation EmployeeDeveloper 51 | - (void)doADaysWork { 52 | [self writeCode]; 53 | } 54 | @end 55 | -------------------------------------------------------------------------------- /chapter_3/item_15.txt: -------------------------------------------------------------------------------- 1 | // Item 15 2 | 3 | // EOCSoundPlayer.h 4 | #import 5 | 6 | @class EOCSoundPlayer; 7 | @protocol EOCSoundPlayerDelegate 8 | - (void)soundPlayerDidFinish:(EOCSoundPlayer*)player; 9 | @end 10 | 11 | @interface EOCSoundPlayer : NSObject 12 | @property (nonatomic, weak) id delegate; 13 | - (id)initWithURL:(NSURL*)url; 14 | - (void)playSound; 15 | @end 16 | 17 | // EOCSoundPlayer.m 18 | #import "EOCSoundPlayer.h" 19 | #import 20 | 21 | void completion(SystemSoundID ssID, void *clientData) { 22 | EOCSoundPlayer *player = (__bridge EOCSoundPlayer*)clientData; 23 | if ([player.delegate respondsToSelector:@selector(soundPlayerDidFinish:)]) { 24 | [player.delegate soundPlayerDidFinish:player]; 25 | } 26 | } 27 | 28 | @implementation EOCSoundPlayer { 29 | SystemSoundID _systemSoundID; 30 | } 31 | 32 | - (id)initWithURL:(NSURL*)url { 33 | if ((self = [super init])) { 34 | AudioServicesCreateSystemSoundID((__bridge CFURLRef)url, &_systemSoundID); 35 | } 36 | return self; 37 | } 38 | 39 | - (void)dealloc { 40 | AudioServicesDisposeSystemSoundID(_systemSoundID); 41 | } 42 | 43 | - (void)playSound { 44 | AudioServicesAddSystemSoundCompletion( 45 | _systemSoundID, 46 | NULL, 47 | NULL, 48 | completion, 49 | (__bridge void*)self); 50 | AudioServicesPlaySystemSound(_systemSoundID); 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /chapter_3/item_16.txt: -------------------------------------------------------------------------------- 1 | // Item 16 2 | 3 | // Rectangle class 4 | #import 5 | 6 | @interface EOCRectangle : NSObject 7 | @property (nonatomic, assign, readonly) float width; 8 | @property (nonatomic, assign, readonly) float height; 9 | @end 10 | 11 | 12 | // Designated initialiser 13 | - (id)initWithWidth:(float)width 14 | andHeight:(float)height 15 | { 16 | if ((self = [super init])) { 17 | _width = width; 18 | _height = height; 19 | } 20 | return self; 21 | } 22 | 23 | 24 | // Overriding super class's designated initialiser 25 | - (id)init { 26 | return [self initWithWidth:5.0f andHeight:10.0f]; 27 | } 28 | 29 | 30 | // Square class with designated initialiser different to its super class 31 | #import "EOCRectangle.h" 32 | 33 | @interface EOCSquare : EOCRectangle 34 | - (id)initWithDimension:(float)dimension; 35 | @end 36 | 37 | @implementation EOCSquare 38 | 39 | - (id)initWithDimension:(float)dimension { 40 | return [super initWithWidth:dimension andHeight:dimension]; 41 | } 42 | 43 | @end 44 | 45 | 46 | // Overriding super class's designated initialiser in EOCSquare 47 | - (id)initWithWidth:(float)width andHeight:(float)height { 48 | float dimension = MAX(width, height); 49 | return [self initWithDimension:dimension]; 50 | } 51 | 52 | 53 | // Throwing exception in overridden superclass designated initialiser 54 | - (id)initWithWidth:(float)width andHeight:(float)height { 55 | @throw [NSException 56 | exceptionWithName:NSInternalInconsistencyException 57 | reason:@"Must use initWithDimension: instead." 58 | userInfo:nil]; 59 | } 60 | 61 | 62 | // Overriding super class's super class designated initialiser 63 | - (id)init { 64 | return [self initWithDimension:5.0f]; 65 | } 66 | 67 | 68 | // Adding another designated initialiser from NSCoding 69 | #import 70 | 71 | @interface EOCRectangle : NSObject 72 | @property (nonatomic, assign, readonly) float width; 73 | @property (nonatomic, assign, readonly) float height; 74 | - (id)initWithWidth:(float)width 75 | andHeight:(float)height; 76 | @end 77 | 78 | @implementation EOCRectangle 79 | 80 | // Designated initialiser 81 | - (id)initWithWidth:(float)width 82 | andHeight:(float)height 83 | { 84 | if ((self = [super init])) { 85 | _width = width; 86 | _height = height; 87 | } 88 | return self; 89 | } 90 | 91 | // Super-class’s designated initialiser 92 | - (id)init { 93 | return [self initWithWidth:5.0f andHeight:10.0f]; 94 | } 95 | 96 | // Initialiser from NSCoding 97 | - (id)initWithCoder:(NSCoder*)decoder { 98 | // Call through to super’s designated initialiser 99 | if ((self = [super init])) { 100 | _width = [decoder decodeFloatForKey:@"width"]; 101 | _height = [decoder decodeFloatForKey:@"height"]; 102 | } 103 | } 104 | 105 | @end 106 | 107 | 108 | // EOCSquare after implementing NSCoding 109 | #import "EOCRectangle.h" 110 | 111 | @interface EOCSquare : EOCRectangle 112 | - (id)initWithDimension:(float)dimension; 113 | @end 114 | 115 | @implementation EOCSquare 116 | 117 | // Designated initialiser 118 | - (id)initWithDimension:(float)dimension { 119 | return [super initWithWidth:dimension andHeight:dimension]; 120 | } 121 | 122 | // Super class designated initialiser 123 | - (id)initWithWidth:(float)width andHeight:(float)height { 124 | float dimension = MAX(width, height); 125 | return [self initWithDimension:dimension]; 126 | } 127 | 128 | // NSCoding designated initialiser 129 | - (id)initWithCoder:(NSCoder*)decoder { 130 | if ((self = [super initWithCoder:decoder])) { 131 | // EOCSquare’s specific initialiser 132 | } 133 | } 134 | 135 | @end 136 | -------------------------------------------------------------------------------- /chapter_3/item_17.txt: -------------------------------------------------------------------------------- 1 | // Item 17 2 | 3 | // Person class 4 | #import 5 | 6 | @interface EOCPerson : NSObject 7 | 8 | @property (nonatomic, copy, readonly) NSString *firstName; 9 | @property (nonatomic, copy, readonly) NSString *lastName; 10 | 11 | - (id)initWithFirstName:(NSString*)firstName 12 | lastName:(NSString*)lastName; 13 | 14 | @end 15 | 16 | @implementation EOCPerson 17 | - (id)initWithFirstName:(NSString*)firstName 18 | lastName:(NSString*)lastName 19 | { 20 | if ((self = [super init])) { 21 | _firstName = [firstName copy]; 22 | _lastName = [lastName copy]; 23 | } 24 | return self; 25 | } 26 | @end 27 | 28 | 29 | // Description method for EOCPerson 30 | - (NSString*)description { 31 | return [NSString stringWithFormat:@"<%@: %p, \"%@ %@\">", 32 | [self class], self, _firstName, _lastName]; 33 | } 34 | 35 | 36 | // Outputting description of an EOCPerson instance 37 | EOCPerson *person = [[EOCPerson alloc] initWithFirstName:@"Bob" lastName:@"Smith"]; 38 | NSLog(@"person = %@", person); 39 | // Output: 40 | // person = 41 | 42 | 43 | // Location class 44 | #import 45 | 46 | @interface EOCLocation : NSObject 47 | @property (nonatomic, copy, readonly) NSString *title; 48 | @property (nonatomic, assign, readonly) float latitude; 49 | @property (nonatomic, assign, readonly) float longitude; 50 | - (id)initWithTitle:(NSString*)title latitude:(float)latitude longitude:(float)longitude; 51 | @end 52 | 53 | @implementation EOCLocation 54 | - (id)initWithTitle:(NSString*)title latitude:(float)latitude longitude:(float)longitude { 55 | if ((self = [super init])) { 56 | _title = [title copy]; 57 | _latitude = latitude; 58 | _longitude = longitude; 59 | } 60 | return self; 61 | } 62 | @end 63 | 64 | 65 | // Description method for EOCLocation 66 | - (NSString*)description { 67 | return [NSString stringWithFormat:@"<%@: %p, %@>", 68 | [self class], 69 | self, 70 | @{@"title":_title, 71 | @"latitude":@(_latitude), 72 | @"longitude":@(_longitude)} 73 | ]; 74 | } 75 | 76 | 77 | // Outputting description of an EOCLocation instance 78 | EOCPerson *person = [[EOCPerson alloc] initWithFirstName:@"Bob" lastName:@"Smith"]; 79 | NSLog(@"person = %@", person); 80 | // Breakpoint here 81 | 82 | 83 | // description vs debugDescription 84 | - (NSString*)description { 85 | return [NSString stringWithFormat:@"%@ %@", _firstName, _lastName]; 86 | } 87 | 88 | - (NSString*)debugDescription { 89 | return [NSString stringWithFormat:@"<%@: %p, \"%@ %@\">", 90 | [self class], self, _firstName, _lastName]; 91 | } 92 | 93 | 94 | // Outputting description of an NSArray instance 95 | NSArray *array = @[@"Effective Objective-C 2.0", @(123), @(YES)]; 96 | NSLog(@"array = %@", array); 97 | // Breakpoint here 98 | -------------------------------------------------------------------------------- /chapter_3/item_18.txt: -------------------------------------------------------------------------------- 1 | // Item 18 2 | 3 | // EOCPointOfInterest object 4 | #import 5 | 6 | @interface EOCPointOfInterest : NSObject 7 | 8 | @property (nonatomic, copy) NSString *identifier; 9 | @property (nonatomic, copy) NSString *title; 10 | @property (nonatomic, assign) float latitude; 11 | @property (nonatomic, assign) float longitude; 12 | 13 | - (id)initWithIdentifier:(NSString*)identifier 14 | title:(NSString*)title 15 | latitude:(float)latitude 16 | longitude:(float)longitude; 17 | 18 | @end 19 | 20 | 21 | // Making everything readonly 22 | #import 23 | 24 | @interface EOCPointOfInterest : NSObject 25 | 26 | @property (nonatomic, copy, readonly) NSString *identifier; 27 | @property (nonatomic, copy, readonly) NSString *title; 28 | @property (nonatomic, assign, readonly) float latitude; 29 | @property (nonatomic, assign, readonly) float longitude; 30 | 31 | - (id)initWithIdentifier:(NSString*)identifier 32 | title:(NSString*)title 33 | latitude:(float)latitude 34 | longitude:(float)longitude; 35 | 36 | @end 37 | 38 | 39 | // Overriding the property scope with the class continuation category 40 | #import "EOCPointOfInterest.h" 41 | 42 | @interface EOCPointOfInterest () 43 | @property (nonatomic, copy, readwrite) NSString *identifier; 44 | @property (nonatomic, copy, readwrite) NSString *title; 45 | @property (nonatomic, assign, readwrite) float latitude; 46 | @property (nonatomic, assign, readwrite) float longitude; 47 | @end 48 | 49 | @implementation EOCPointOfInterest 50 | … 51 | @end 52 | 53 | 54 | // Person class 55 | // EOCPerson.h 56 | #import 57 | 58 | @interface EOCPerson : NSObject 59 | 60 | @property (nonatomic, copy, readonly) NSString *firstName; 61 | @property (nonatomic, copy, readonly) NSString *lastName; 62 | @property (nonatomic, strong, readonly) NSSet *friends; 63 | 64 | - (id)initWithFirstName:(NSString*)firstName 65 | andLastName:(NSString*)lastName; 66 | - (void)addFriend:(EOCPerson*)person; 67 | - (void)removeFriend:(EOCPerson*)person; 68 | 69 | @end 70 | 71 | // EOCPerson.m 72 | #import "EOCPerson.h" 73 | 74 | @interface EOCPerson () 75 | @property (nonatomic, copy, readwrite) NSString *firstName; 76 | @property (nonatomic, copy, readwrite) NSString *lastName; 77 | @end 78 | 79 | @implementation EOCPerson { 80 | NSMutableSet *_internalFriends 81 | } 82 | 83 | - (NSSet*)friends { 84 | return [_internalFriends copy]; 85 | } 86 | 87 | - (void)addFriend:(EOCPerson*)person { 88 | [_internalFriends addObject:person]; 89 | } 90 | 91 | - (void)removeFriend:(EOCPerson*)person { 92 | [_internalFriends removeObject:person]; 93 | } 94 | 95 | - (id)initWithFirstName:(NSString*)firstName 96 | andLastName:(NSString*)lastName { 97 | if ((self = [super init])) { 98 | _firstName = firstName; 99 | _lastName = lastName; 100 | _internalFriends = [NSMutableSet new]; 101 | } 102 | return self; 103 | } 104 | 105 | @end 106 | 107 | 108 | // Introspection of returned class. Not to be done! 109 | EOCPerson *person = …; 110 | NSSet *friends = person.friends; 111 | if ([friends isKindOfClass:[NSMutableSet class]]) { 112 | NSMutableSet *mutableFriends = (NSMutableSet*)friends; 113 | /* mutate the set */ 114 | } 115 | -------------------------------------------------------------------------------- /chapter_3/item_19.txt: -------------------------------------------------------------------------------- 1 | // Item 19 2 | 3 | // Replacing string with string in string 4 | NSString *text = @"The quick brown fox jumped over the lazy dog"; 5 | NSString *newText = [text stringByReplacingOccurrencesOfString:@"fox" withString:@"cat"]; 6 | 7 | 8 | // C++ example of naming 9 | class Rectangle { 10 | public: 11 | Rectangle(float width, float height); 12 | float getWidth(); 13 | float getHeight(); 14 | private: 15 | float width; 16 | float height; 17 | }; 18 | 19 | Rectangle *aRectangle = new Rectangle(5.0f, 10.0f); 20 | 21 | 22 | // Objective-C equivalent 23 | #import 24 | 25 | @interface EOCRectangle : NSObject 26 | 27 | @property (nonatomic, assign, readonly) float width; 28 | @property (nonatomic, assign, readonly) float height; 29 | 30 | - (id)initWithSize:(float)width :(float)height; 31 | 32 | @end 33 | 34 | EOCRectangle *aRectangle = 35 | [[EOCRectangle alloc] initWithSize:5.0f :10.0f]; 36 | 37 | 38 | // Better initialiser name 39 | - (id)initWithWidth:(float)width andHeight:(float)height; 40 | EOCRectangle *aRectangle = 41 | [[EOCRectangle alloc] initWithWidth:5.0f andHeight:10.0f]; 42 | 43 | 44 | // Well named methods 45 | - (EOCRectangle*)unionRectangle:(EOCRectangle*)rectangle 46 | - (float)area 47 | 48 | 49 | // Badly named methods 50 | - (EOCRectangle*)union:(EOCRectangle*)rectangle // Unclear 51 | - (float)calculateTheArea // Too verbose 52 | -------------------------------------------------------------------------------- /chapter_3/item_20.txt: -------------------------------------------------------------------------------- 1 | // Item 20 2 | 3 | // Private method prefixing 4 | #import 5 | 6 | @interface EOCObject : NSObject 7 | - (void)publicMethod; 8 | @end 9 | 10 | @implementation EOCObject 11 | 12 | - (void)publicMethod { 13 | … 14 | } 15 | 16 | - (void)p_privateMethod { 17 | … 18 | } 19 | 20 | @end 21 | 22 | 23 | // Example overriding UIViewController method 24 | #import 25 | 26 | @interface EOCViewController : UIViewController 27 | @end 28 | 29 | @implementation EOCViewController 30 | - (void)_resetViewController { 31 | // Reset state and views 32 | } 33 | @end 34 | -------------------------------------------------------------------------------- /chapter_3/item_21.txt: -------------------------------------------------------------------------------- 1 | // Item 21 2 | 3 | // Throwing exception 4 | id someResource = …; 5 | if ( /* check for error */ ) { 6 | @throw [NSException exceptionWithName:@"ExceptionName" 7 | reason:@"There was an error" 8 | userInfo:nil]; 9 | } 10 | [someResource doSomething]; 11 | [someResource release]; 12 | 13 | 14 | // Throwing exception from a method that must be overridden 15 | - (void)mustOverrideMethod { 16 | @throw [NSException 17 | exceptionWithName:NSInternalInconsistencyException 18 | reason:[NSString stringWithFormat:@"%@ must be overridden", _cmd] 19 | userInfo:nil]; 20 | } 21 | 22 | 23 | // Returning nil in case of error in initialiser 24 | - (id)initWithValue:(id)value { 25 | if ((self = [super init])) { 26 | if ( /* Value means instance can’t be created */ ) { 27 | self = nil; 28 | } else { 29 | // Initialise instance 30 | } 31 | } 32 | return self; 33 | } 34 | 35 | 36 | // Checking for return error 37 | NSError *error = nil; 38 | BOOL ret = [object doSomethingError:&error]; 39 | if (error) { 40 | // There was an error 41 | } 42 | 43 | 44 | // Not bothering with the error parameter 45 | BOOL ret = [object doSomethingError:nil]; 46 | if (ret) { 47 | // There was an error 48 | } 49 | 50 | 51 | // Returning the error 52 | - (BOOL)doSomethingError:(NSError**)error { 53 | // Do something 54 | 55 | NSError *returnError = nil; 56 | if (/* there was an error */) { 57 | if (error) { 58 | *error = [NSError errorWithDomain:domain 59 | code:code 60 | userInfo:userInfo]; 61 | } 62 | return YES; 63 | } else { 64 | return NO; 65 | } 66 | } 67 | 68 | 69 | // Defining the error domain and codes 70 | // EOCErrors.h 71 | extern NSString *const EOCErrorDomain; 72 | 73 | typedef NS_ENUM(NSUInteger, EOCError) { 74 | EOCErrorUnknown = −1, 75 | EOCErrorInternalInconsistency = 100, 76 | EOCErrorGeneralFault = 105, 77 | EOCErrorBadInput = 500, 78 | }; 79 | 80 | // EOCErrors.m 81 | NSString *const EOCErrorDomain = @"EOCErrorDomain"; 82 | -------------------------------------------------------------------------------- /chapter_3/item_22.txt: -------------------------------------------------------------------------------- 1 | // Item 22 2 | 3 | // Person object 4 | #import 5 | 6 | @interface EOCPerson : NSObject 7 | 8 | @property (nonatomic, copy, readonly) NSString *firstName; 9 | @property (nonatomic, copy, readonly) NSString *lastName; 10 | 11 | - (id)initWithFirstName:(NSString*)firstName 12 | andLastName:(NSString*)lastName; 13 | 14 | @end 15 | 16 | 17 | // NSCopying implementation 18 | - (id)copyWithZone:(NSZone*)zone { 19 | Person *copy = [[[self class] allocWithZone:zone] 20 | initWithFirstName:_firstName 21 | andLastName:_lastName]; 22 | return copy; 23 | } 24 | 25 | 26 | // Person class with friends set, implementing NSCopying 27 | #import 28 | 29 | @interface EOCPerson : NSObject 30 | 31 | @property (nonatomic, copy, readonly) NSString *firstName; 32 | @property (nonatomic, copy, readonly) NSString *lastName; 33 | 34 | - (id)initWithFirstName:(NSString*)firstName 35 | andLastName:(NSString*)lastName; 36 | 37 | - (void)addFriend:(EOCPerson*)person; 38 | - (void)removeFriend:(EOCPerson*)person; 39 | 40 | @end 41 | 42 | @implementation EOCPerson { 43 | NSMutableSet *_friends; 44 | } 45 | 46 | - (id)initWithFirstName:(NSString*)firstName 47 | andLastName:(NSString*)lastName { 48 | if ((self = [super init])) { 49 | _firstName = [firstName copy]; 50 | _lastName = [lastName copy]; 51 | _friends = [NSMutableSet new]; 52 | } 53 | return self; 54 | } 55 | 56 | - (void)addFriend:(EOCPerson*)person { 57 | [_friends addObject:person]; 58 | } 59 | 60 | - (void)removeFriend:(EOCPerson*)person { 61 | [_friends removeObject:person]; 62 | } 63 | 64 | - (id)copyWithZone:(NSZone*)zone { 65 | EOCPerson *copy = [[[self class] allocWithZone:zone] 66 | initWithFirstName:_firstName 67 | andLastName:_lastName]; 68 | copy->_friends = [_friends mutableCopy]; 69 | return copy; 70 | } 71 | 72 | @end 73 | 74 | 75 | // Deep copy of EOCPerson object 76 | - (id)deepCopy { 77 | EOCPerson *copy = [[[self class] allocWithZone:zone] 78 | initWithFirstName:_firstName 79 | andLastName:_lastName]; 80 | copy->_friends = [[NSSet alloc] initWithSet:_friends 81 | copyItems:YES]; 82 | return copy; 83 | } 84 | -------------------------------------------------------------------------------- /chapter_4/item_23.txt: -------------------------------------------------------------------------------- 1 | // Item 23 2 | 3 | // Network fetcher delegate 4 | @protocol EOCNetworkFetcherDelegate 5 | - (void)networkFetcher:(EOCNetworkFetcher*)fetcher 6 | didReceiveData:(NSData*)data; 7 | - (void)networkFetcher:(EOCNetworkFetcher*)fetcher 8 | didFailWithError:(NSError*)error; 9 | @end 10 | 11 | 12 | // Property for delegate 13 | @interface EOCNetworkFetcher : NSObject 14 | @property (nonatomic, weak) id delegate; 15 | @end 16 | 17 | 18 | // Implementing the delegate 19 | @implementation EOCDataModel () 20 | @end 21 | 22 | @implementation EOCDataModel 23 | - (void)networkFetcher:(EOCNetworkFetcher*)fetcher 24 | didReceiveData:(NSData*)data { 25 | /* Handle data */ 26 | } 27 | - (void)networkFetcher:(EOCNetworkFetcher*)fetcher 28 | didFailWithError:(NSError*)error { 29 | /* Handle error */ 30 | } 31 | @end 32 | 33 | 34 | // Optional methods 35 | @protocol EOCNetworkFetcherDelegate 36 | @optional 37 | - (void)networkFetcher:(EOCNetworkFetcher*)fetcher 38 | didReceiveData:(NSData*)data; 39 | - (void)networkFetcher:(NetworkFetcher*)fetcher 40 | didFailWithError:(EOCNSError*)error; 41 | @end 42 | 43 | 44 | // Checking if the delegate responds to selector 45 | NSData *data = /* data obtained from network */; 46 | if ([_delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)]) { 47 | [_delegate networkFetcher:self didReceiveData:data]; 48 | } 49 | 50 | 51 | // Checking which network fetcher caused the callback 52 | - (void)networkFetcher:(EOCNetworkFetcher*)fetcher 53 | didReceiveData:(NSData*)data { 54 | if (fetcher == _myFetcherA) { 55 | /* Handle data */ 56 | } else if (fetcher == _myFetcherB) { 57 | /* Handle data */ 58 | } 59 | } 60 | 61 | 62 | // Extended protocol 63 | @protocol EOCNetworkFetcherDelegate 64 | @optional 65 | - (void)networkFetcher:(EOCNetworkFetcher*)fetcher 66 | didReceiveData:(NSData*)data; 67 | - (void)networkFetcher:(EOCNetworkFetcher*)fetcher 68 | didFailWithError:(NSError*)error; 69 | - (void)networkFetcher:(EOCNetworkFetcher*)fetcher 70 | didUpdateProgressTo:(float)progress; 71 | @end 72 | 73 | 74 | // Bitfield struct 75 | struct data { 76 | unsigned int fieldA : 8; 77 | unsigned int fieldB : 4; 78 | unsigned int fieldC : 2; 79 | unsigned int fieldD : 1; 80 | }; 81 | 82 | 83 | // Flags to indicate what methods the delegate responds to 84 | @interface EOCNetworkFetcher () { 85 | struct { 86 | unsigned int didReceiveData : 1; 87 | unsigned int didFailWithError : 1; 88 | unsigned int didUpdateProgressTo : 1; 89 | } _delegateFlags; 90 | } 91 | @end 92 | 93 | 94 | // Setting and checking flags 95 | // Set flag 96 | _delegateFlags.didReceiveData = 1; 97 | 98 | // Check flag 99 | if (_delegateFlags.didReceiveData) { 100 | // Yes, flag set 101 | } 102 | 103 | 104 | // Setting delegate and setting flags 105 | - (void)setDelegate:(id 5 | 6 | @interface EOCPerson : NSObject 7 | @property (nonatomic, copy, readonly) NSString *firstName; 8 | @property (nonatomic, copy, readonly) NSString *lastName; 9 | @property (nonatomic, strong, readonly) NSArray *friends; 10 | 11 | - (id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName; 12 | 13 | /* Friendship methods */ 14 | - (void)addFriend:(EOCPerson*)person; 15 | - (void)removeFriend:(EOCPerson*)person; 16 | - (BOOL)isFriendsWith:(EOCPerson*)person; 17 | 18 | /* Work methods */ 19 | - (void)performDaysWork; 20 | - (void)takeVacationFromWork; 21 | 22 | /* Play methods */ 23 | - (void)goToTheCinema; 24 | - (void)goToSportsGame; 25 | 26 | @end 27 | 28 | 29 | // Splitting EOCPerson into categories 30 | #import 31 | 32 | @interface EOCPerson : NSObject 33 | @property (nonatomic, copy, readonly) NSString *firstName; 34 | @property (nonatomic, copy, readonly) NSString *lastName; 35 | @property (nonatomic, strong, readonly) NSArray *friends; 36 | 37 | - (id)initWithFirstName:(NSString*)firstName 38 | andLastName:(NSString*)lastName; 39 | @end 40 | 41 | @interface EOCPerson (Friendship) 42 | - (void)addFriend:(EOCPerson*)person; 43 | - (void)removeFriend:(EOCPerson*)person; 44 | - (BOOL)isFriendsWith:(EOCPerson*)person; 45 | @end 46 | 47 | @interface EOCPerson (Work) 48 | - (void)performDaysWork; 49 | - (void)takeVacationFromWork; 50 | @end 51 | 52 | @interface EOCPerson (Play) 53 | - (void)goToTheCinema; 54 | - (void)goToSportsGame; 55 | @end 56 | 57 | 58 | // Friendship category 59 | // EOCPerson+Friendship.h 60 | #import "EOCPerson.h" 61 | 62 | @interface EOCPerson (Friendship) 63 | - (void)addFriend:(EOCPerson*)person; 64 | - (void)removeFriend:(EOCPerson*)person; 65 | - (BOOL)isFriendsWith:(EOCPerson*)person; 66 | @end 67 | 68 | // EOCPerson+Friendship.m 69 | #import "EOCPerson+Friendship.h" 70 | 71 | @implementation EOCPerson (Friendship) 72 | - (void)addFriend:(EOCPerson*)person { 73 | … 74 | } 75 | - (void)removeFriend:(EOCPerson*)person { 76 | … 77 | } 78 | - (BOOL)isFriendsWith:(EOCPerson*)person { 79 | … 80 | } 81 | @end 82 | -------------------------------------------------------------------------------- /chapter_4/item_25.txt: -------------------------------------------------------------------------------- 1 | // Item 25 2 | 3 | // Category on NSString 4 | @interface NSString (HTTP) 5 | 6 | // Encode a string with URL encoding 7 | - (NSString*)urlEncodedString; 8 | 9 | // Decode a URL encoded string 10 | - (NSString*)urlDecodedString; 11 | 12 | @end 13 | 14 | 15 | // Namespacing the category 16 | @interface NSString (ABC_HTTP) 17 | 18 | // Encode a string with URL encoding 19 | - (NSString*)abc_urlEncodedString; 20 | 21 | // Decode a URL encoded string 22 | - (NSString*)abc_urlDecodedString; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /chapter_4/item_26.txt: -------------------------------------------------------------------------------- 1 | // Item 26 2 | 3 | // Person class with friendship category 4 | #import 5 | 6 | @interface EOCPerson : NSObject 7 | @property (nonatomic, copy, readonly) NSString *firstName; 8 | @property (nonatomic, copy, readonly) NSString *lastName; 9 | 10 | - (id)initWithFirstName:(NSString*)firstName 11 | andLastName:(NSString*)lastName; 12 | @end 13 | 14 | @implementation EOCPerson 15 | // Methods 16 | @end 17 | 18 | @interface EOCPerson (Friendship) 19 | @property (nonatomic, strong) NSArray *friends; 20 | - (BOOL)isFriendsWith:(EOCPerson*)person; 21 | @end 22 | 23 | @implementation EOCPerson (Friendship) 24 | // Methods 25 | @end 26 | 27 | 28 | // Implementing property accessors in category 29 | #import 30 | 31 | static const char* kFriendsPropertyKey = "kFriendsPropertyKey"; 32 | 33 | @implementation EOCPerson (Friendship) 34 | 35 | - (NSArray*)friends { 36 | return objc_getAssociatedObject(self, kFriendsPropertyKey); 37 | } 38 | 39 | - (void)setFriends:(NSArray*)friends { 40 | objc_setAssociatedObject(self, kFriendsPropertyKey, friends, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 41 | } 42 | 43 | @end 44 | 45 | 46 | // Readonly property in category 47 | @interface NSCalendar (EOC_Additions) 48 | @property (nonatomic, strong, readonly) NSArray *eoc_allMonths; 49 | @end 50 | 51 | @implementation NSCalendar (EOC_Additions) 52 | - (NSArray*)eoc_allMonths { 53 | if ([self.identifier isEqualToString:NSGregorianCalendar]) { 54 | return @[@"January", @"February", @"March", @"April", @"May", @"June", @"July", @"August", @"September", @"October", @"November", @"December"]; 55 | } else if (/* other calendar identifiers */( { 56 | /* return months for other calendars */ 57 | } 58 | } 59 | @end 60 | -------------------------------------------------------------------------------- /chapter_4/item_27.txt: -------------------------------------------------------------------------------- 1 | // Item 27 2 | 3 | // Secret instance variable 4 | #import 5 | 6 | @class EOCSuperSecretClass 7 | 8 | @interface EOCClass : NSObject { 9 | @private 10 | EOCSuperSecretClass *_secretInstance; 11 | } 12 | @end 13 | 14 | 15 | // Using class continuation category to hide secret instance variable 16 | // EOCClass.h 17 | #import 18 | @interface EOCClass : NSObject 19 | @end 20 | 21 | // EOCClass.m 22 | #import "EOCClass.h" 23 | #import "EOCSuperSecretClass.h" 24 | 25 | @interface EOCClass () { 26 | EOCSuperSecretClass *_secretInstance; 27 | } 28 | @end 29 | 30 | @implementation EOCClass 31 | // Methods here 32 | @end 33 | 34 | 35 | // C++ instance variable 36 | // EOCClass.h 37 | #import 38 | 39 | #include "SomeCppClass.h" 40 | 41 | @interface EOCClass : NSObject { 42 | @private 43 | SomeCppClass _cppClass; 44 | } 45 | @end 46 | 47 | 48 | // Forward declaring C++ class 49 | // EOCClass.h 50 | #import 51 | 52 | class SomeCppClass; 53 | 54 | @interface EOCClass : NSObject { 55 | @private 56 | SomeCppClass *_cppClass; 57 | } 58 | @end 59 | 60 | 61 | // Using class continuation category to hide C++ class 62 | // EOCClass.h 63 | #import 64 | 65 | @interface EOCClass : NSObject 66 | @end 67 | 68 | // EOCClass.mm 69 | #import "EOCClass.h" 70 | #include "SomeCppClass.h" 71 | 72 | @interface EOCClass () { 73 | SomeCppClass _cppClass; 74 | } 75 | @end 76 | 77 | @implementation EOCClass 78 | @end 79 | 80 | 81 | // Readonly properties on class 82 | #import 83 | 84 | @interface EOCPerson : NSObject 85 | @property (nonatomic, copy, readonly) NSString *firstName; 86 | @property (nonatomic, copy, readonly) NSString *lastName; 87 | 88 | - (id)initWithFirstName:(NSString*)firstName 89 | lastName:(NSString*)lastName; 90 | @end 91 | 92 | 93 | // Redeclaring properties as readwrite 94 | @interface EOCPerson () 95 | @property (nonatomic, copy, readwrite) NSString *firstName; 96 | @property (nonatomic, copy, readwrite) NSString *lastName; 97 | @end 98 | 99 | 100 | // Secret delegate in public interface 101 | #import 102 | #import "EOCSecretDelegate.h" 103 | 104 | @interface EOCPerson : NSObject 105 | @property (nonatomic, copy, readonly) NSString *firstName; 106 | @property (nonatomic, copy, readonly) NSString *lastName; 107 | 108 | - (id)initWithFirstName:(NSString*)firstName 109 | lastName:(NSString*)lastName; 110 | @end 111 | 112 | 113 | // Moving declaration of conformance to protocol to class continuation category 114 | #import "EOCPerson.h" 115 | #import "EOCSecretDelegate.h" 116 | 117 | @interface EOCPerson () 118 | @end 119 | 120 | @implementation EOCPerson 121 | … 122 | @end 123 | -------------------------------------------------------------------------------- /chapter_4/item_28.txt: -------------------------------------------------------------------------------- 1 | // Item 28 2 | 3 | // Database connection protocol 4 | @protocol EOCDatabaseConnection 5 | - (void)connect; 6 | - (void)disconnect; 7 | - (BOOL)isConnected; 8 | - (NSArray*)performQuery:(NSString*)query; 9 | @end 10 | 11 | 12 | // Database manager class 13 | #import 14 | 15 | @protocol EOCDatabaseConnection; 16 | 17 | @interface EOCDatabaseManager : NSObject 18 | + (id)sharedInstance; 19 | - (id)connectionWithIdentifier:(NSString*)identifier; 20 | @end 21 | 22 | 23 | // Fetched results controller with section info 24 | NSFetchedResultsController *controller = /* some controller */; 25 | NSUInteger section = /* section index to query */; 26 | 27 | NSArray *sections = controller.sections; 28 | id sectionInfo = sections[section]; 29 | NSUInteger numberOfObjects = sectionInfo.numberOfObjects; 30 | -------------------------------------------------------------------------------- /chapter_5/item_29.txt: -------------------------------------------------------------------------------- 1 | // Item 29 2 | 3 | // Simple example of reference counting in action 4 | NSMutableArray *array = [[NSMutableArray alloc] init]; 5 | 6 | NSNumber *number = [[NSNumber alloc] initWithInt:1337]; 7 | [array addObject:number]; 8 | [number release]; 9 | 10 | // do something with `array’ 11 | 12 | [array release]; 13 | 14 | 15 | // Example of using an object after you have released your reference to it. Bad idea. 16 | NSNumber *number = [[NSNumber alloc] initWithInt:1337]; 17 | [array addObject:number]; 18 | [number release]; 19 | NSLog(@"number = %@", number); 20 | 21 | 22 | // Setting the variable to nil after releasing so that you don't accidentally use an unsafe variable. 23 | NSNumber *number = [[NSNumber alloc] initWithInt:1337]; 24 | [array addObject:number]; 25 | [number release]; 26 | number = nil; 27 | 28 | 29 | // Memory management semantics of a strong setter accessor. 30 | - (void)setFoo:(id)foo { 31 | [foo retain]; 32 | [_foo release]; 33 | _foo = foo; 34 | } 35 | 36 | 37 | // `stringValue' method without an autorelease - incorrect semantics as per method name. 38 | - (NSString*)stringValue { 39 | NSString *str = [[NSString alloc] initWithFormat:@"I am this: %@", self]; 40 | return str; 41 | } 42 | 43 | 44 | // `stringValue' method with an autorelease. 45 | - (NSString*)stringValue { 46 | NSString *str = [[NSString alloc] initWithFormat:@"I am this: %@", self]; 47 | return [str autorelease]; 48 | } 49 | 50 | 51 | // Using a value that's returned autoreleased. 52 | NSString *str = [self stringValue]; 53 | NSLog(@"The string is: %@", str); 54 | 55 | 56 | // Retaining an object returned autoreleased if required. 57 | _instanceVariable = [[self stringValue] retain]; 58 | // … 59 | [_instanceVariable release]; 60 | -------------------------------------------------------------------------------- /chapter_5/item_30.txt: -------------------------------------------------------------------------------- 1 | // Item 30 2 | 3 | // Example method written under ARC. 4 | if ([self shouldLogMessage]) { 5 | NSString *message = [[NSString alloc] initWithFormat:@"I am object, %p", self]; 6 | NSLog(@"message = %@", message); 7 | } 8 | 9 | 10 | // Equivalent method showing the `release' call added by ARC. 11 | if ([self shouldLogMessage]) { 12 | NSString *message = [[NSString alloc] initWithFormat:@"I am object, %p", self]; 13 | NSLog(@"message = %@", message); 14 | [message release]; ///< Added by ARC 15 | } 16 | 17 | 18 | // Examples showing what ARC does depending on the method name. 19 | - (EOCPerson*)newPerson { 20 | EOCPerson *person = [[EOCPerson alloc] init]; 21 | return person; 22 | /** 23 | * The method name begins with `new’, and since `person’ 24 | * already has an unbalanced +1 reference count from the 25 | * `alloc’, no retains, releases or autoreleases are 26 | * required when returning. 27 | */ 28 | } 29 | 30 | - (EOCPerson*)somePerson { 31 | EOCPerson *person = [[EOCPerson alloc] init]; 32 | return person; 33 | /** 34 | * The method name does not begin with one of the "owning" 35 | * prefixes, therefore ARC will add an autorelease when 36 | * returning `person’. 37 | * The equivalent manual reference counting statement is: 38 | * return [person autorelease]; 39 | */ 40 | } 41 | 42 | - (void)doSomething { 43 | EOCPerson *personOne = [self newPerson]; 44 | // … 45 | 46 | EOCPerson *personTwo = [self somePerson]; 47 | // … 48 | 49 | /** 50 | * At this point, `personOne’ and `personTwo’ go out of 51 | * scope, therefore ARC needs to clean them up as required. 52 | * - `personOne’ was returned as owned by this block of 53 | code, so it needs to be released. 54 | * - `personTwo’ was returned not owned by this block of 55 | code, so it does not need to be released. 56 | * The equivalent manual reference counting cleanup code 57 | * is: 58 | * [personOne release]; 59 | */ 60 | } 61 | 62 | 63 | // Setting an instance variable under ARC. 64 | // From a class where _myPerson is a strong instance variable 65 | _myPerson = [EOCPerson personWithName:@"Bob Smith"]; 66 | 67 | 68 | // Showing what ARC adds for the above code. 69 | EOCPerson *tmp = [EOCPerson personWithName:@"Bob Smith"]; 70 | _myPerson = [tmp retain]; 71 | 72 | 73 | // Showing the C calls emitted by ARC for returning autoreleased and retaining an autoreleased return value. 74 | // Within EOCPerson class 75 | + (EOCPerson*)personWithName:(NSString*)name { 76 | EOCPerson *person = [[EOCPerson alloc] init]; 77 | person.name = name; 78 | objc_autoreleaseReturnValue(person); 79 | } 80 | 81 | // Code using EOCPerson class 82 | EOCPerson *tmp = [self somePerson]; 83 | _myPerson = objc_retainAutoreleasedReturnValue(tmp); 84 | 85 | 86 | // Pseudocode of autoreleased return value methods 87 | id objc_autoreleaseReturnValue(id object) { 88 | if (return_code_will_retain_object) { 89 | set_flag(object); 90 | } else { 91 | return [object autorelease]; 92 | } 93 | } 94 | 95 | id objc_retainAutoreleasedReturnValue(id object) { 96 | if (get_flag(object)) { 97 | clear_flag(); 98 | } else { 99 | return [object retain]; 100 | } 101 | } 102 | 103 | 104 | // Illustrating memory management semantics of instance variables 105 | @interface EOCClass : NSObject { 106 | id _object; 107 | } 108 | 109 | @implementation EOCClass 110 | - (void)setup { 111 | _object = [EOCOtherClass new]; 112 | } 113 | @end 114 | 115 | 116 | // Showing memory management calls added by ARC in `setup' method 117 | - (void)setup { 118 | id tmp = [EOCOtherClass new]; 119 | _object = [tmp retain]; 120 | [tmp release]; 121 | } 122 | 123 | 124 | // Setter under manual reference counting, exhibiting typical mistake 125 | - (void)setObject:(id)object { 126 | [_object release]; 127 | _object = [object retain]; 128 | } 129 | 130 | 131 | // Setter under ARC, guaranteed to be safe 132 | - (void)setObject:(id)object { 133 | _object = object; 134 | } 135 | 136 | 137 | // __weak and __unsafe_unretained attributes 138 | @interface EOCClass : NSObject { 139 | id __weak _weakObject; 140 | id __unsafe_unretained _unsafeUnretainedObject; 141 | } 142 | 143 | 144 | // __weak local variable 145 | NSURL *url = [NSURL URLWithString:@"http://www.example.com/"]; 146 | EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 147 | EOCNetworkFetcher * __weak weakFetcher = fetcher; 148 | [fetcher startWithCompletion:^(BOOL success){ 149 | NSLog(@"Finished fetching from %@", weakFetcher.url); 150 | }]; 151 | 152 | 153 | // `dealloc' method under manual reference counting 154 | - (void)dealloc { 155 | [_foo release]; 156 | [_bar release]; 157 | [super dealloc]; 158 | } 159 | 160 | 161 | // `dealloc' method under ARC 162 | - (void)dealloc { 163 | CFRelease(_coreFoundationObject); 164 | free(_heapAllocatedMemoryBlob); 165 | } 166 | -------------------------------------------------------------------------------- /chapter_5/item_31.txt: -------------------------------------------------------------------------------- 1 | // Item 31 2 | 3 | // Releasing CF objects and removing observer in `dealloc' 4 | - (void)dealloc { 5 | CFRelease(coreFoundationObject); 6 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 7 | } 8 | 9 | 10 | // Class for connecting to a server, which will use a scarce system resource - file descriptor, etc 11 | #import 12 | 13 | @interface EOCServerConnection : NSObject 14 | - (void)open:(NSString*)address; 15 | - (void)close; 16 | @end 17 | 18 | 19 | // `close' and `dealloc' methods 20 | - (void)close { 21 | /* clean up resources */ 22 | _closed = YES; 23 | } 24 | 25 | - (void)dealloc { 26 | if (!_closed) { 27 | NSLog(@"ERROR: close was not called before object was deallocated!"); 28 | [self close]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter_5/item_32.txt: -------------------------------------------------------------------------------- 1 | // Item 32 2 | 3 | // @try/@catch block under manual reference counting 4 | @try { 5 | EOCSomeClass *object = [[EOCSomeClass alloc] init]; 6 | [object doSomethingThatMayThrow]; 7 | [object release]; 8 | } 9 | @catch (...) { 10 | NSLog(@"Whoops, there was an error. Oh well, it wasn’t important."); 11 | } 12 | 13 | 14 | // Fixing the potential leak 15 | EOCSomeClass *object; 16 | @try { 17 | object = [[EOCSomeClass alloc] init]; 18 | [object doSomethingThatMayThrow]; 19 | } 20 | @catch (...) { 21 | NSLog(@"Whoops, there was an error. Oh well, it wasn’t important."); 22 | } 23 | @finally { 24 | [object release]; 25 | } 26 | 27 | 28 | // @try/@catch block under ARC 29 | @try { 30 | EOCSomeClass *object = [[EOCSomeClass alloc] init]; 31 | [object doSomethingThatMayThrow]; 32 | } 33 | @catch (...) { 34 | NSLog(@"Whoops, there was an error. Oh well, it wasn’t important."); 35 | } 36 | -------------------------------------------------------------------------------- /chapter_5/item_33.txt: -------------------------------------------------------------------------------- 1 | // Item 33 2 | 3 | // Object model with potential retain cycle 4 | #import 5 | 6 | @class EOCClassA; 7 | @class EOCClassB; 8 | 9 | @interface EOCClassA : NSObject 10 | @property (nonatomic, strong) EOCClassB *other; 11 | @end 12 | 13 | @interface EOCClassB : NSObject 14 | @property (nonatomic, strong) EOCClassA *other; 15 | @end 16 | 17 | 18 | // Fixing retain cycle with weak reference 19 | #import 20 | 21 | @class EOCClassA; 22 | @class EOCClassB; 23 | 24 | @interface EOCClassA : NSObject 25 | @property (nonatomic, strong) EOCClassB *other; 26 | @end 27 | 28 | @interface EOCClassB : NSObject 29 | @property (nonatomic, unsafe_unretained) EOCClassA *other; 30 | @end 31 | -------------------------------------------------------------------------------- /chapter_5/item_34.txt: -------------------------------------------------------------------------------- 1 | // Item 34 2 | 3 | // Switching selector 4 | SEL selector; 5 | if (/* some condition */) { 6 | selector = @selector(foo); 7 | } else if (/* some other condition */) { 8 | selector = @selector(bar); 9 | } else { 10 | selector = @selector(baz); 11 | } 12 | [object performSelector:selector]; 13 | 14 | 15 | // Uh, oh. What memory management semantics to use on `ret'? 16 | SEL selector; 17 | if (/* some condition */) { 18 | selector = @selector(newObject); 19 | } else if (/* some other condition */) { 20 | selector = @selector(copy); 21 | } else { 22 | selector = @selector(someProperty); 23 | } 24 | id ret = [object performSelector:selector]; 25 | 26 | 27 | // Example of calling performSelector:withObject: 28 | id object = /* an object with a property called `value’ */; 29 | id newValue = /* new value for the property */; 30 | [object performSelector:@selector(setValue:) withObject:newValue]; 31 | -------------------------------------------------------------------------------- /chapter_5/item_35.txt: -------------------------------------------------------------------------------- 1 | // Item 35 2 | 3 | // @autoreleasepool block in main.m 4 | int main(int argc, char *argv[]) { 5 | @autoreleasepool { 6 | return UIApplicationMain(argc, argv, nil, @"EOCAppDelegate"); 7 | } 8 | } 9 | 10 | 11 | // Nested pools 12 | @autoreleasepool { 13 | NSString *string = [NSString stringWithFormat:@"1 = %i", 1]; 14 | @autoreleasepool { 15 | NSNumber *number = [NSNumber numberWithInt:1]; 16 | } 17 | } 18 | 19 | 20 | // Loop which may cause a lot of autoreleases to happen 21 | for (int i = 0; i < 100000; i++) { 22 | [self doSomethingWithInt:i]; 23 | } 24 | 25 | 26 | // Another loop that might create lots of autoreleased objects 27 | NSArray *databaseRecords = …; 28 | NSMutableArray *people = [NSMutableArray new]; 29 | for (NSDictionary *record in databaseRecords) { 30 | EOCPerson *person = [[EOCPerson alloc] initWithRecord:record]; 31 | [people addObject:person]; 32 | } 33 | 34 | 35 | // Reducing high memory waterline with appropriately places @autoreleasepool 36 | NSArray *databaseRecords = …; 37 | NSMutableArray *people = [NSMutableArray new]; 38 | for (NSDictionary *record in databaseRecords) { 39 | @autoreleasepool { 40 | EOCPerson *person = [[EOCPerson alloc] initWithRecord:record]; 41 | [people addObject:person]; 42 | } 43 | } 44 | 45 | 46 | // Old autorelease pool style 47 | NSArray *databaseRecords = …; 48 | NSMutableArray *people = [NSMutableArray new]; 49 | int i = 0; 50 | 51 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 52 | for (NSDictionary *record in databaseRecords) { 53 | EOCPerson *person = [[EOCPerson alloc] initWithRecord:record]; 54 | [people addObject:person]; 55 | 56 | // Drain the pool only every 10 cycles 57 | if (++i == 10) { 58 | [pool drain]; 59 | } 60 | } 61 | 62 | // Also drain at the end incase the loop is not a multiple of 10 63 | [pool drain]; 64 | 65 | 66 | // Problem with old style pools 67 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 68 | id object = [self createObject]; 69 | [pool drain]; 70 | [self useObject:object]; 71 | 72 | 73 | // Problem cannot happen with new pools 74 | @autoreleasepool { 75 | id object = [self createObject]; 76 | } 77 | [self useObject:object]; 78 | -------------------------------------------------------------------------------- /chapter_5/item_36.txt: -------------------------------------------------------------------------------- 1 | // Item 36 2 | 3 | // Showing how zombies work 4 | #import 5 | #import 6 | 7 | @interface EOCClass : NSObject 8 | @end 9 | 10 | @implementation EOCClass 11 | @end 12 | 13 | void PrintClassInfo(id obj) { 14 | Class cls = object_getClass(obj); 15 | Class superCls = class_getSuperclass(cls); 16 | NSLog(@"=== %s : %s ===", class_getName(cls), class_getName(superCls)); 17 | } 18 | 19 | int main(int argc, char *argv[]) { 20 | EOCClass *obj = [[EOCClass alloc] init]; 21 | NSLog(@"Before release:"); 22 | PrintClassInfo(obj); 23 | 24 | [obj release]; 25 | NSLog(@"After release:"); 26 | PrintClassInfo(obj); 27 | } 28 | 29 | 30 | // "Deallocating" an object pseudocode with zombies turned on 31 | // Obtain the class of the object being deallocated 32 | Class cls = object_getClass(self); 33 | 34 | // Get the class’s name 35 | const char *clsName = class_getName(cls); 36 | 37 | // Prepend _NSZombie_ to the class name 38 | const char *zombieClsName = "_NSZombie_" + clsName; 39 | 40 | // See if the specific zombie class exists 41 | Class zombieCls = objc_lookUpClass(zombieClsName); 42 | 43 | // If the specific zombie class doesn’t exist, then it needs to be created 44 | if (!zombieCls) { 45 | // Obtain the template zombie class called _NSZombie_ 46 | Class baseZombieCls = objc_lookUpClass("_NSZombie_"); 47 | 48 | // Duplicate the base zombie class, where the new class’s name is the prepended string from above 49 | zombieCls = objc_duplicateClass(baseZombieCls, zombieClsName, 0); 50 | } 51 | 52 | // Perform normal destruction of the object being deallocated 53 | objc_destructInstance(self); 54 | 55 | // Set the class of the object being deallocated to the zombie class 56 | objc_setClass(self, zombieCls); 57 | 58 | // The class of `self’ is now _NSZombie_OriginalClass 59 | 60 | 61 | // Forwarding mechanism when a zombie is detected 62 | // Obtain the object’s class 63 | Class cls = object_getClass(self); 64 | 65 | // Get the class’s name 66 | const char *clsName = class_getName(cls); 67 | 68 | // Check if the class is prefixed with _NSZombie_ 69 | if (string_has_prefix(clsName, "_NSZombie_") { 70 | // If so, this object is a zombie 71 | 72 | // Get the original class name by skipping past the _NSZombie_, i.e. taking the substring from character 10 73 | const char *originalClsName = substring_from(clsName, 10); 74 | 75 | // Get the selector name of the message 76 | const char *selectorName = sel_getName(_cmd); 77 | 78 | // Log a message to indicate which selector is being sent to which zombie 79 | Log("*** -[%s %s]: message sent to deallocated instance %p", originalClsName, selectorName, self); 80 | 81 | // Kill the application 82 | abort(); 83 | } 84 | 85 | 86 | // Showing zombies in action 87 | EOCClass *obj = [[EOCClass alloc] init]; 88 | NSLog(@"Before release:"); 89 | PrintClassInfo(obj); 90 | 91 | [obj release]; 92 | NSLog(@"After release:"); 93 | PrintClassInfo(obj); 94 | 95 | NSString *desc = [obj description]; 96 | -------------------------------------------------------------------------------- /chapter_5/item_37.txt: -------------------------------------------------------------------------------- 1 | // Item 37 2 | 3 | // Don't do this 4 | while ([object retainCount]) { 5 | [object release]; 6 | } 7 | 8 | 9 | // Large "retain counts" of some objects (singletons) 10 | NSString *string = @"Some string"; 11 | NSLog(@"string retainCount = %lu", [string retainCount]); 12 | 13 | NSNumber *numberI = @1; 14 | NSLog(@"numberI retainCount = %lu", [numberI retainCount]); 15 | 16 | NSNumber *numberF = @3.141f; 17 | NSLog(@"numberF retainCount = %lu", [numberF retainCount]); 18 | 19 | 20 | // Showing retain count might not necessarily be what you expect 21 | id object = [self createObject]; 22 | [opaqueObject doSomethingWithObject:object]; 23 | NSLog(@"retainCount = %lu", [object retainCount]); 24 | -------------------------------------------------------------------------------- /chapter_6/item_38.txt: -------------------------------------------------------------------------------- 1 | // Item 38 2 | 3 | // Block to add two numbers together 4 | int (^addBlock)(int a, int b) = ^(int a, int b){ 5 | return a + b; 6 | }; 7 | 8 | 9 | // Using the `addBlock' 10 | int (^addBlock)(int a, int b) = ^(int a, int b){ 11 | return a + b; 12 | }; 13 | int add = addBlock(2, 5); //< add = 7 14 | 15 | 16 | // Using the enclosing scope 17 | int additional = 5; 18 | int (^addBlock)(int a, int b) = ^(int a, int b){ 19 | return a + b + additional; 20 | }; 21 | int add = addBlock(2, 5); //< add = 12 22 | 23 | 24 | // __block variables 25 | NSArray *array = @[@0, @1, @2, @3, @4, @5]; 26 | __block NSInteger count = 0; 27 | [array enumerateObjectsUsingBlock:^(NSNumber *number, NSUInteger idx, BOOL *stop){ 28 | if ([number compare:@2] == NSOrderedAscending) { 29 | count++; 30 | } 31 | }]; 32 | // count = 2 33 | 34 | 35 | // Capturing instance variables 36 | @interface EOCClass 37 | … 38 | - (void)anInstanceMethod { 39 | // … 40 | void (^someBlock)() = ^{ 41 | _anInstanceVariable = @"Something"; 42 | NSLog(@"_anInstanceVariable = %@", _anInstanceVariable); 43 | }; 44 | // … 45 | } 46 | … 47 | @end 48 | 49 | 50 | // Unsafe! 51 | void (^block)(); 52 | if (/* some condition */) { 53 | block = ^{ 54 | NSLog(@"Block A"); 55 | }; 56 | } else { 57 | block = ^{ 58 | NSLog(@"Block B"); 59 | }; 60 | } 61 | block(); 62 | 63 | 64 | // Ah, safe. 65 | void (^block)(); 66 | if (/* some condition */) { 67 | block = [^{ 68 | NSLog(@"Block A"); 69 | } copy]; 70 | } else { 71 | block = [^{ 72 | NSLog(@"Block B"); 73 | } copy]; 74 | } 75 | block(); 76 | -------------------------------------------------------------------------------- /chapter_6/item_39.txt: -------------------------------------------------------------------------------- 1 | // Item 39 2 | 3 | // Block with an int return type 4 | ^(BOOL flag, int value){ 5 | if (flag) { 6 | return value * 5; 7 | } else { 8 | return value * 10; 9 | } 10 | } 11 | 12 | 13 | // Storing a block to a variable 14 | int (^variableName)(BOOL flag, int value) = ^(BOOL flag, int value){ 15 | // Implementation 16 | } 17 | 18 | 19 | // Simple typedef 20 | typedef int(^EOCSomeBlock)(BOOL flag, int value); 21 | 22 | 23 | // Using the typedef 24 | EOCSomeBlock block = ^(BOOL flag, int value){ 25 | // Implementation 26 | }; 27 | 28 | 29 | // Complicated method signature 30 | - (void)startWithCompletionHandler:(void(^)(NSData *data, NSError *error))completion; 31 | 32 | 33 | // Simplified with typedef 34 | typedef void(^EOCCompletionHandler)(NSData *data, NSError *error); 35 | - (void)startWithCompletionHandler:(EOCCompletionHandler)completion; 36 | 37 | 38 | // Changing the typedef 39 | typedef void(^EOCCompletionHandler)(NSData *data, NSTimeInterval duration, NSError *error); 40 | -------------------------------------------------------------------------------- /chapter_6/item_40.txt: -------------------------------------------------------------------------------- 1 | // Item 40 2 | 3 | // Network fetcher class using delegation 4 | #import 5 | 6 | @class EOCNetworkFetcher; 7 | @protocol EOCNetworkFetcherDelegate 8 | - (void)networkFetcher:(EOCNetworkFetcher*)networkFetcher didFinishWithData:(NSData*)data; 9 | @end 10 | 11 | @interface EOCNetworkFetcher : NSObject 12 | @property (nonatomic, weak) id delegate; 13 | - (id)initWithURL:(NSURL*)url; 14 | - (void)start; 15 | @end 16 | 17 | 18 | // Using the network fetcher class with delegation 19 | - (void)fetchFooData { 20 | NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/foo.dat"]; 21 | EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 22 | fetcher.delegate = self; 23 | [fetcher start]; 24 | } 25 | 26 | // … 27 | 28 | - (void)networkFetcher:(EOCNetworkFetcher*)networkFetcher didFinishWithData:(NSData*)data { 29 | _fetchedFooData = data; 30 | } 31 | 32 | 33 | // Simplifying with a completion handler block 34 | #import 35 | 36 | typedef void(^EOCNetworkFetcherCompletionHandler)(NSData *data); 37 | 38 | @interface EOCNetworkFetcher : NSObject 39 | - (id)initWithURL:(NSURL*)url; 40 | - (void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)handler; 41 | @end 42 | 43 | 44 | // Using the completion handler block API - simpler 45 | - (void)fetchFooData { 46 | NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/foo.dat"]; 47 | EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 48 | [fetcher startWithCompletionHandler:^(NSData *data){ 49 | _fetchedFooData = data; 50 | }]; 51 | } 52 | 53 | 54 | // Multiple fetchers with delegation 55 | - (void)fetchFooData { 56 | NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/foo.dat"]; 57 | _fooFetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 58 | _fooFetcher.delegate = self; 59 | [_fooFetcher start]; 60 | } 61 | 62 | - (void)fetchBarData { 63 | NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/bar.dat"]; 64 | _fooFetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 65 | _fooFetcher.delegate = self; 66 | [_fooFetcher start]; 67 | } 68 | 69 | - (void)networkFetcher:(EOCNetworkFetcher*)networkFetcher didFinishWithData:(NSData*)data { 70 | if (networkFetcher == _fooFetcher) { 71 | _fetchedFooData = data; 72 | _fooFetcher = nil; 73 | } else if (networkFetcher == _barFetcher) { 74 | _fetchedBarData = data; 75 | _barFetcher = nil; 76 | } 77 | // etc 78 | } 79 | 80 | 81 | // Multiple fetchers with completion handler block 82 | - (void)fetchFooData { 83 | NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/foo.dat"]; 84 | EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 85 | [fetcher startWithCompletionHandler:^(NSData *data){ 86 | _fetchedFooData = data; 87 | }]; 88 | } 89 | 90 | - (void)fetchBarData { 91 | NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/bar.dat"]; 92 | EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 93 | [fetcher startWithCompletionHandler:^(NSData *data){ 94 | _fetchedBarData = data; 95 | }]; 96 | } 97 | 98 | 99 | // Separate completion & error 100 | #import 101 | 102 | @class EOCNetworkFetcher; 103 | typedef void(^EOCNetworkFetcherCompletionHandler)(NSData *data); 104 | typedef void(^EOCNetworkFetcherErrorHandler)(NSError *error); 105 | 106 | @interface EOCNetworkFetcher : NSObject 107 | - (id)initWithURL:(NSURL*)url; 108 | - (void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)completion 109 | failureHandler:(EOCNetworkFetcherErrorHandler)failure; 110 | @end 111 | 112 | 113 | // Using separate completion & error 114 | EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 115 | [fetcher startWithCompletionHander:^(NSData *data){ 116 | // Handle success 117 | } 118 | failureHandler:^(NSError *error){ 119 | // Handle failure 120 | }]; 121 | 122 | 123 | // Single completion, encapsulating error 124 | #import 125 | 126 | @class EOCNetworkFetcher; 127 | typedef void(^EOCNetworkFetcherCompletionHandler)(NSData *data, NSError *error); 128 | 129 | @interface EOCNetworkFetcher : NSObject 130 | - (id)initWithURL:(NSURL*)url; 131 | - (void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)completion; 132 | @end 133 | 134 | 135 | // Using single completion 136 | EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 137 | [fetcher startWithCompletionHander:^(NSData *data, NSError *error){ 138 | if (error) { 139 | // Handle failure 140 | } else { 141 | // Handle success 142 | } 143 | }]; 144 | 145 | 146 | // Progress handler 147 | typedef void(^EOCNetworkFetcherCompletionHandler)(float progress); 148 | @property (nonatomic, copy) EOCNetworkFetcherProgressHandler progressHandler; 149 | -------------------------------------------------------------------------------- /chapter_6/item_41.txt: -------------------------------------------------------------------------------- 1 | // Item 41 2 | 3 | // Network fetcher 4 | // EOCNetworkFetcher.h 5 | #import 6 | 7 | typedef void(^EOCNetworkFetcherCompletionHandler)(NSData *data); 8 | 9 | @interface EOCNetworkFetcher : NSObject 10 | @property (nonatomic, strong, readonly) NSURL *url; 11 | - (id)initWithURL:(NSURL*)url; 12 | - (void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)completion; 13 | @end 14 | 15 | 16 | // EOCNetworkFetcher.m 17 | #import "EOCNetworkFetcher.h" 18 | 19 | @interface EOCNetworkFetcher () 20 | @property (nonatomic, strong, readwrite) NSURL *url; 21 | @property (nonatomic, copy) EOCNetworkFetcherCompletionHandler completionHandler; 22 | @property (nonatomic, strong) NSData *downloadedData; 23 | @end 24 | 25 | @implementation EOCNetworkFetcher 26 | 27 | - (void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)completion { 28 | self.completionHandler = completion; 29 | // Start the request 30 | // Request sets downloadedData property 31 | // When request is finished, p_requestCompleted is called 32 | } 33 | 34 | - (void)p_requestCompleted { 35 | if (_completionHandler) { 36 | _completionHandler(_downloadedData); 37 | } 38 | } 39 | 40 | @end 41 | 42 | 43 | // Using the network fetcher 44 | @implementation EOCClass { 45 | EOCNetworkFetcher *_networkFetcher; 46 | NSData *_fetchedData; 47 | } 48 | 49 | - (void)downloadData { 50 | NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"]; 51 | _networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 52 | [_networkFetcher startWithCompletionHandler:^(NSData *data){ 53 | NSLog(@"Request URL %@ finished", _networkFetcher.url); 54 | _fetchedData = data; 55 | }]; 56 | } 57 | @end 58 | 59 | 60 | // Breaking retain cycle 61 | [_networkFetcher startWithCompletionHandler:^(NSData* data){ 62 | NSLog(@"Request URL %@ finished", _networkFetcher.url); 63 | _fetchedData = data; 64 | _networkFetcher = nil; 65 | } 66 | 67 | 68 | // Another retain cycle 69 | - (void)downloadData { 70 | NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"]; 71 | EOCNetworkFetcher *networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 72 | [networkFetcher startWithCompletionHandler:^(NSData *data){ 73 | NSLog(@"Request URL %@ finished", networkFetcher.url); 74 | _fetchedData = data; 75 | }]; 76 | } 77 | 78 | 79 | // Breaking retain cycle by clearing completion handler 80 | - (void)p_requestCompleted { 81 | if (_completionHandler) { 82 | _completionHandler(_downloadedData); 83 | } 84 | self.completionHandler = nil; 85 | } 86 | -------------------------------------------------------------------------------- /chapter_6/item_42.txt: -------------------------------------------------------------------------------- 1 | // Item 42 2 | 3 | // @synchronized block 4 | - (void)synchronisedMethod { 5 | @synchronized(self) { 6 | // Safe 7 | } 8 | } 9 | 10 | 11 | // NSLock 12 | _lock = [[NSLock alloc] init]; 13 | 14 | - (void)synchronisedMethod { 15 | [_lock lock]; 16 | // Safe 17 | [_lock unlock]; 18 | } 19 | 20 | 21 | // Synchronised accessors 22 | - (NSString*)someString { 23 | @synchronized(self) { 24 | return _someString; 25 | } 26 | } 27 | 28 | - (void)setSomeString:(NSString*)someString { 29 | @synchronized(self) { 30 | _someString = someString; 31 | } 32 | } 33 | 34 | 35 | // Using GCD queue for synchronisation 36 | _syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL); 37 | 38 | // … 39 | 40 | - (NSString*)someString { 41 | __block NSString *localSomeString; 42 | dispatch_sync(_syncQueue, ^{ 43 | localSomeString = _someString; 44 | }); 45 | return localSomeString; 46 | } 47 | 48 | - (void)setSomeString:(NSString*)someString { 49 | dispatch_sync(_syncQueue, ^{ 50 | _someString = someString; 51 | }); 52 | } 53 | 54 | 55 | // Making the setter an asynchronous dispatch 56 | - (void)setSomeString:(NSString*)someString { 57 | dispatch_async(_syncQueue, ^{ 58 | _someString = someString; 59 | }); 60 | } 61 | 62 | 63 | // Using a concurrent queue 64 | _syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 65 | 66 | // … 67 | 68 | - (NSString*)someString { 69 | __block NSString *localSomeString; 70 | dispatch_sync(_syncQueue, ^{ 71 | localSomeString = _someString; 72 | }); 73 | return localSomeString; 74 | } 75 | 76 | - (void)setSomeString:(NSString*)someString { 77 | dispatch_async(_syncQueue, ^{ 78 | _someString = someString; 79 | }); 80 | } 81 | 82 | 83 | // Making the concurrent approach work with barriers 84 | _syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 85 | 86 | // … 87 | 88 | - (NSString*)someString { 89 | __block NSString *localSomeString; 90 | dispatch_sync(_syncQueue, ^{ 91 | localSomeString = _someString; 92 | }); 93 | return localSomeString; 94 | } 95 | 96 | - (void)setSomeString:(NSString*)someString { 97 | dispatch_barrier_async(_syncQueue, ^{ 98 | _someString = someString; 99 | }); 100 | } 101 | -------------------------------------------------------------------------------- /chapter_6/item_43.txt: -------------------------------------------------------------------------------- 1 | // Item 43 2 | 3 | -------------------------------------------------------------------------------- /chapter_6/item_44.txt: -------------------------------------------------------------------------------- 1 | // Item 44 2 | 3 | // Waiting on a group of tasks 4 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 5 | dispatch_group_t dispatchGroup = dispatch_group_create(); 6 | 7 | for (id object in collection) { 8 | dispatch_group_async(dispatchGroup, 9 | queue, 10 | ^{ 11 | [object performTask]; 12 | }); 13 | } 14 | 15 | dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER); 16 | 17 | // Continue processing after completing tasks 18 | 19 | 20 | // Using `dispatch_notify' 21 | dispatch_queue_t notifyQueue = dispatch_get_main_queue(); 22 | dispatch_group_notify(dispatchGroup, 23 | notifyQueue, 24 | ^{ 25 | // Continue processing after completing tasks 26 | }); 27 | 28 | 29 | // Multiple queues, same group 30 | dispatch_queue_t lowPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); 31 | dispatch_queue_t highPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); 32 | dispatch_group_t dispatchGroup = dispatch_group_create(); 33 | 34 | for (id object in lowPriorityObjects) { 35 | dispatch_group_async(dispatchGroup, 36 | lowPriorityQueue, 37 | ^{ 38 | [object performTask]; 39 | }); 40 | } 41 | 42 | for (id object in highPriorityObjects) { 43 | dispatch_group_async(dispatchGroup, 44 | highPriorityQueue, 45 | ^{ 46 | [object performTask]; 47 | }); 48 | } 49 | 50 | dispatch_queue_t notifyQueue = dispatch_get_main_queue(); 51 | dispatch_group_notify(dispatchGroup, 52 | notifyQueue, 53 | ^{ 54 | // Continue processing after completing tasks 55 | }); 56 | 57 | 58 | // Similar thing to groups, using serial queue 59 | dispatch_queue_t queue = dispatch_queue_create("com.effectiveobjectivec.queue", NULL); 60 | 61 | for (id object in collection) { 62 | dispatch_async(queue, 63 | ^{ 64 | [object performTask]; 65 | }); 66 | } 67 | 68 | dispatch_async(queue, 69 | ^{ 70 | // Continue processing after completing tasks 71 | }); 72 | 73 | 74 | 75 | // `dispatch_apply' instead of a group 76 | dispatch_queue_t queue = dispatch_queue_create("com.effectiveobjectivec.queue", NULL); 77 | dispatch_apply(10, queue, ^(size_t i){ 78 | // Perform task 79 | }); 80 | 81 | 82 | // Same example as above but this time using `dispatch_apply' 83 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 84 | 85 | dispatch_apply(array.count, queue, ^(size_t i){ 86 | id object = array[i]; 87 | [object performTask]; 88 | }); 89 | -------------------------------------------------------------------------------- /chapter_6/item_45.txt: -------------------------------------------------------------------------------- 1 | // Item 45 2 | 3 | // Old style singleton initialisation 4 | @implementation EOCClass 5 | 6 | + (id)sharedInstance { 7 | static EOCClass *sharedInstance = nil; 8 | @synchronized(self) { 9 | if (!sharedInstance) { 10 | sharedInstance = [[self alloc] init]; 11 | } 12 | } 13 | return sharedInstance; 14 | } 15 | 16 | // … 17 | 18 | @end 19 | 20 | 21 | // `dispatch_once' singleton initialisation 22 | + (id)sharedInstance { 23 | static EOCClass *sharedInstance = nil; 24 | static dispatch_once_t onceToken; 25 | dispatch_once(&onceToken, ^{ 26 | sharedInstance = [[self alloc] init]; 27 | }); 28 | return sharedInstance; 29 | } 30 | -------------------------------------------------------------------------------- /chapter_6/item_46.txt: -------------------------------------------------------------------------------- 1 | // Item 46 2 | 3 | // GCD locked accessors 4 | - (NSString*)someString { 5 | __block NSString *localSomeString; 6 | dispatch_sync(_syncQueue, ^{ 7 | localSomeString = _someString; 8 | }); 9 | return localSomeString; 10 | } 11 | 12 | - (void)setSomeString:(NSString*)someString { 13 | dispatch_async(_syncQueue, ^{ 14 | _someString = someString; 15 | }); 16 | } 17 | 18 | 19 | // Attempting to fix the problem of `dispatch_sync' maybe blocking 20 | - (NSString*)someString { 21 | __block NSString *localSomeString; 22 | dispatch_block_t accessorBlock = ^{ 23 | localSomeString = _someString; 24 | }; 25 | 26 | if (dispatch_get_current_queue() == _syncQueue) { 27 | accessorBlock(); 28 | } else { 29 | dispatch_sync(_syncQueue, accessorBlock); 30 | } 31 | 32 | return localSomeString; 33 | } 34 | 35 | 36 | // Explaining deadlock 37 | dispatch_queue_t queueA = dispatch_queue_create("com.effectiveobjectivec.queueA", NULL); 38 | dispatch_queue_t queueB = dispatch_queue_create("com.effectiveobjectivec.queueB", NULL); 39 | 40 | dispatch_sync(queueA, ^{ 41 | dispatch_sync(queueB, ^{ 42 | dispatch_sync(queueA, ^{ 43 | // Deadlock 44 | }); 45 | }); 46 | }); 47 | 48 | 49 | // Explaining why checking the current queue does not always fix the deadlock 50 | dispatch_sync(queueA, ^{ 51 | dispatch_sync(queueB, ^{ 52 | dispatch_block_t block = ^{ /* … */ }; 53 | if (dispatch_get_current_queue() == queueA) { 54 | block(); 55 | } else { 56 | dispatch_sync(queueA, block); 57 | } 58 | }); 59 | }); 60 | 61 | 62 | // Queue specific data example 63 | dispatch_queue_t queueA = dispatch_queue_create("com.effectiveobjectivec.queueA", NULL); 64 | dispatch_queue_t queueB = dispatch_queue_create("com.effectiveobjectivec.queueB", NULL); 65 | dispatch_set_target_queue(queueB, queueA); 66 | 67 | static int kQueueSpecific; 68 | CFStringRef queueSpecificValue = CFSTR("queueA"); 69 | dispatch_queue_set_specific(queueA, 70 | &kQueueSpecific, 71 | (void*)queueSpecificValue, 72 | (dispatch_function_t)CFRelease); 73 | 74 | dispatch_sync(queueB, ^{ 75 | dispatch_block_t block = ^{ NSLog(@"No deadlock!"); }; 76 | 77 | CFStringRef retrievedValue = dispatch_get_specific(&kQueueSpecific); 78 | if (retrievedValue) { 79 | block(); 80 | } else { 81 | dispatch_sync(queueA, block); 82 | } 83 | }); 84 | -------------------------------------------------------------------------------- /chapter_7/item_47.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/effectiveobjc/code/09b2a42c3131c6f3edd6d72f6cecd4daeed3d569/chapter_7/item_47.txt -------------------------------------------------------------------------------- /chapter_7/item_48.txt: -------------------------------------------------------------------------------- 1 | // Item 48 2 | 3 | // For loops 4 | NSArray *anArray = /* … */; 5 | for (int i = 0; i < anArray.count; i++) { 6 | id object = anArray[i]; 7 | // Do something with `object’ 8 | } 9 | 10 | NSDictionary *aDictionary = /* … */; 11 | NSArray *keys = [aDictionary allKeys]; 12 | for (int i = 0; i < keys.count; i++) { 13 | id key = keys[i]; 14 | id value = aDictionary[key]; 15 | // Do something with `key’ and `value’ 16 | } 17 | 18 | NSSet *aSet = /* … */; 19 | NSArray *objects = [aSet allObjects]; 20 | for (int i = 0; i < objects.count; i++) { 21 | id object = objects[i]; 22 | // Do something with `object’ 23 | } 24 | 25 | 26 | // NSEnumerator 27 | NSArray *anArray = /* … */; 28 | NSEnumerator *enumerator = [anArray objectEnumerator]; 29 | while ((id object = [enumerator nextObject]) != nil) { 30 | // Do something with `object’ 31 | } 32 | 33 | NSDictionary *aDictionary = /* … */; 34 | NSEnumerator *enumerator = [aDictionary keyEnumerator]; 35 | while ((id key = [enumerator nextObject]) != nil) { 36 | id value = aDictionary[key]; 37 | // Do something with `key’ and `value’ 38 | } 39 | 40 | NSSet *aSet = /* … */; 41 | NSEnumerator *enumerator = [aSet objectEnumerator]; 42 | while ((id object = [enumerator nextObject]) != nil) { 43 | // Do something with `object’ 44 | } 45 | 46 | 47 | // Reverse NSEnumerator 48 | NSArray *anArray = /* … */; 49 | NSEnumerator *enumerator = [anArray revserseObjectEnumerator]; 50 | while ((id object = [enumerator nextObject]) != nil) { 51 | // Do something with `object’ 52 | } 53 | 54 | 55 | // Fast enumeration 56 | NSArray *anArray = /* … */; 57 | for (id object in anArray) { 58 | // Do something with `object’ 59 | } 60 | 61 | NSDictionary *aDictionary = /* … */; 62 | for (id key in aDictionary) { 63 | id value = aDictionary[key]; 64 | // Do something with `key’ and `value’ 65 | } 66 | 67 | NSSet *aSet = /* … */; 68 | for (id object in aSet) { 69 | // Do something with `object’ 70 | } 71 | 72 | 73 | // Reverse fast enumeration 74 | NSArray *anArray = /* … */; 75 | for (id object in [anArray reverseObjectEnumerator]) { 76 | // Do something with `object’ 77 | } 78 | 79 | 80 | // Block enumeration 81 | NSArray *anArray = /* … */; 82 | [anArray enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop){ 83 | // Do something with `object’ 84 | if (shouldStop) { 85 | *stop = YES; 86 | } 87 | }]; 88 | 89 | NSDictionary *aDictionary = /* … */; 90 | [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id object, NSUInteger idx, BOOL *stop){ 91 | // Do something with `key’ and `object’ 92 | if (shouldStop) { 93 | *stop = YES; 94 | } 95 | }]; 96 | 97 | NSSet *aSet = /* … */; 98 | [aSet enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop){ 99 | // Do something with `object’ 100 | if (shouldStop) { 101 | *stop = YES; 102 | } 103 | }]; 104 | 105 | 106 | // Casting in the block signature 107 | NSDictionary *aDictionary = /* … */; 108 | [aDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *object, NSUInteger idx, BOOL *stop){ 109 | // Do something with `key’ and `object’ 110 | }]; 111 | -------------------------------------------------------------------------------- /chapter_7/item_49.txt: -------------------------------------------------------------------------------- 1 | // Item 49 2 | 3 | // Simple example 4 | NSArray *anNSArray = @[@1, @2, @3, @4, @5]; 5 | CFArrayRef aCFArray = (__bridge CFArrayRef)anNSArray; 6 | NSLog(@"Size of array = %li", CFArrayGetCount(aCFArray)); 7 | 8 | 9 | // Non-copying keys 10 | #import 11 | #import 12 | 13 | const void *EOCRetainCallback(CFAllocatorRef allocator, 14 | const void *value) 15 | { 16 | return CFRetain(value); 17 | } 18 | 19 | void EOCReleaseCallback(CFAllocatorRef allocator, 20 | const void *value) 21 | { 22 | CFRelease(value); 23 | } 24 | 25 | CFDictionaryKeyCallBacks keyCallbacks = { 26 | 0, 27 | EOCRetainCallback, 28 | EOCReleaseCallback, 29 | NULL, 30 | CFEqual, 31 | CFHash 32 | }; 33 | 34 | CFDictionaryValueCallBacks valueCallbacks = { 35 | 0, 36 | EOCRetainCallback, 37 | EOCReleaseCallback, 38 | NULL, 39 | CFEqual 40 | }; 41 | 42 | CFMutableDictionaryRef aCFDictionary = CFDictionaryCreateMutable(NULL, 0, &keyCallbacks, &valueCallbacks); 43 | 44 | NSMutableDictionary *anNSDictionary = (__bridge_transfer NSMutableDictionary*)aCFDictionary; 45 | -------------------------------------------------------------------------------- /chapter_7/item_50.txt: -------------------------------------------------------------------------------- 1 | // Item 50 2 | 3 | // Using NSCache 4 | #import 5 | 6 | // Network fetcher class 7 | typedef void(^EOCNetworkFetcherCompletionHandler)(NSData *data); 8 | @interface EOCNetworkFetcher : NSObject 9 | - (id)initWithURL:(NSURL*)url; 10 | - (void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)handler; 11 | @end 12 | 13 | // Class that uses the network fetcher and caches results 14 | @interface EOCClass : NSObject 15 | @end 16 | 17 | @implementation EOCClass { 18 | NSCache *_cache; 19 | } 20 | 21 | - (id)init { 22 | if ((self = [super init])) { 23 | _cache = [NSCache new]; 24 | 25 | // Cache a maximum of 100 URLs 26 | _cache.countLimit = 100; 27 | 28 | /** 29 | * The size in bytes of data is used as the cost, 30 | * so this sets a cost limit of 5MB. 31 | */ 32 | _cache.totalCostLimit = 5 * 1024 * 1024; 33 | } 34 | return self; 35 | } 36 | 37 | - (void)downloadDataForURL:(NSURL*)url { 38 | NSData *cachedData = [_cache objectForKey:url]; 39 | if (cachedData) { 40 | // Cache hit 41 | [self useData:cachedData]; 42 | } else { 43 | // Cache miss 44 | EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 45 | [fetcher startWithCompletionHandler:^(NSData *data){ 46 | [_cache setObject:data forKey:url cost:data.length]; 47 | [self useData:data]; 48 | }]; 49 | } 50 | } 51 | 52 | @end 53 | 54 | 55 | // Using NSPurgeableData 56 | - (void)downloadDataForURL:(NSURL*)url { 57 | NSPurgeableData *cachedData = [_cache objectForKey:url]; 58 | if (cachedData) { 59 | // Stop the data being purged 60 | [cacheData beginContentAccess]; 61 | 62 | [self useData:cachedData]; 63 | 64 | // Mark that the data may be purged again 65 | [cacheData endContentAccess]; 66 | } else { 67 | // Cache miss 68 | EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url]; 69 | [fetcher startWithCompletionHandler:^(NSData *data){ 70 | NSPurgeableData *purgeableData = [NSPurgeableData dataWithData:data]; 71 | [_cache setObject:purgeableData forKey:url cost:purgeableData.length]; 72 | 73 | // Don’t need to beginContentAccess as it begins with access already marked 74 | 75 | [self useData:data]; 76 | 77 | // Mark that the data may be purged now 78 | [purgeableData endContentAccess]; 79 | }]; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /chapter_7/item_51.txt: -------------------------------------------------------------------------------- 1 | // Item 51 2 | 3 | // Simple load example 4 | #import 5 | 6 | #import "EOCClassA.h" //< From the same library 7 | 8 | @interface EOCClassB : NSObject 9 | @end 10 | 11 | @implementation EOCClassB 12 | + (void)load { 13 | NSLog(@"Loading EOCClassB"); 14 | EOCClassA *object = [EOCClassA new]; 15 | // Use `object’ 16 | } 17 | @end 18 | 19 | 20 | // Simple initialize example 21 | #import 22 | 23 | @interface EOCBaseClass : NSObject 24 | @end 25 | 26 | @implementation EOCBaseClass 27 | + (void)initialize { 28 | NSLog(@"%@ initialize", self); 29 | } 30 | @end 31 | 32 | @interface EOCSubClass : EOCBaseClass 33 | @end 34 | 35 | @implementation EOCSubClass 36 | @end 37 | 38 | 39 | // Checking the class in initialize 40 | + (void)initialize { 41 | if (self == [EOCBaseClass class]) { 42 | NSLog(@"%@ initialized", self); 43 | } 44 | } 45 | 46 | 47 | // Chicken and egg 48 | #import 49 | 50 | static id EOCClassAInternalData; 51 | @interface EOCClassA : NSObject 52 | @end 53 | 54 | static id EOCClassBInternalData; 55 | @interface EOCClassB : NSObject 56 | @end 57 | 58 | @implementation EOCClassA 59 | 60 | + (void)initialize { 61 | if (self == [EOCClassA class]) { 62 | [EOCClassB doSomethingThatUsesItsInternalData]; 63 | EOCClassAInternalData = [self setupInternalData]; 64 | } 65 | } 66 | 67 | @end 68 | 69 | @implementation EOCClassB 70 | 71 | + (void)initialize { 72 | if (self == [EOCClassB class]) { 73 | [EOCClassA doSomethingThatUsesItsInternalData]; 74 | EOCClassBInternalData = [self setupInternalData]; 75 | } 76 | } 77 | 78 | @end 79 | 80 | 81 | // Lean initialize 82 | // EOCClass.h 83 | #import 84 | 85 | @interface EOCClass : NSObject 86 | @end 87 | 88 | // EOCClass.m 89 | #import "EOCClass.h" 90 | 91 | static const int kInterval = 10; 92 | static NSMutableArray *kSomeObjects; 93 | 94 | @implementation EOCClass 95 | + (void)initialize { 96 | if (self == [EOCClass class]) { 97 | kSomeObjects = [NSMutableArray new]; 98 | } 99 | } 100 | @end 101 | -------------------------------------------------------------------------------- /chapter_7/item_52.txt: -------------------------------------------------------------------------------- 1 | // Item 52 2 | 3 | // Example showing retain cycle 4 | #import 5 | 6 | @interface EOCClass : NSObject 7 | - (void)startPolling; 8 | - (void)stopPolling; 9 | @end 10 | 11 | @implementation EOCClass { 12 | NSTimer *_pollTimer; 13 | } 14 | 15 | - (id)init { 16 | return [super init]; 17 | } 18 | 19 | - (void)dealloc { 20 | [_pollTimer invalidate]; 21 | } 22 | 23 | - (void)stopPolling { 24 | [_pollTimer invalidate]; 25 | _pollTimer = nil; 26 | } 27 | 28 | - (void)startPolling { 29 | _pollTimer = 30 | [NSTimer scheduledTimerWithTimeInterval:5.0 31 | target:self 32 | selector:@selector(p_doPoll) 33 | userInfo:nil 34 | repeats:YES]; 35 | } 36 | 37 | - (void)p_doPoll { 38 | // Poll the resource 39 | } 40 | 41 | @end 42 | 43 | 44 | // Block support for NSTimer 45 | #import 46 | 47 | @interface NSTimer (EOCBlocksSupport) 48 | 49 | + (void)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval 50 | block:(void(^)())block 51 | repeats:(BOOL)repeats; 52 | 53 | @end 54 | 55 | @implementation NSTimer (EOCBlocksSupport) 56 | 57 | + (void)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval 58 | block:(void(^)())block 59 | repeats:(BOOL)repeats 60 | { 61 | return [self scheduledTimerWithTimeInterval:interval 62 | target:self 63 | selector:@selector(eoc_blockInvoke:) 64 | userInfo:[block copy] 65 | repeats:repeats]; 66 | } 67 | 68 | + (void)eoc_blockInvoke:(NSTimer*)timer { 69 | void (^block)() = timer.userInfo; 70 | if (block) { 71 | block(); 72 | } 73 | } 74 | 75 | @end 76 | 77 | 78 | // Changing to use the block - still a cycle 79 | - (void)startPolling { 80 | _pollTimer = 81 | [NSTimer eoc_scheduledTimerWithTimeInterval:5.0 82 | block:^{ 83 | [self p_doPoll]; 84 | } 85 | repeats:YES]; 86 | } 87 | 88 | 89 | // No cycle using weak references 90 | - (void)startPolling { 91 | __weak EOCClass *weakSelf = self; 92 | _pollTimer = 93 | [NSTimer eoc_scheduledTimerWithTimeInterval:5.0 94 | block:^{ 95 | EOCClass *strongSelf = weakSelf; 96 | [strongSelf p_doPoll]; 97 | } 98 | repeats:YES]; 99 | } 100 | --------------------------------------------------------------------------------