网上看到的 http://esoftmobile.com/2013/08/10/effective-objective-c/

本文是针对《Effective Objective-C》一书的代码解读,笔者并没有看过原书,只是通过阅读该书的代码,并结合相应的主题,来臆测作者可能要表达的内容并用自己的语言来描述出来。

Chapter 1: Accustoming Yourself to Objective-C

Item 1: Familiarize Yourself with Objective-C's Roots

Item 2: Minimize Importing Headers in Headers

减少头文件中引入(#import)文件的数量,大部分情况下我们应该在头文件中用@class申明要引用的类,在实现文件中涉及到对该类的操作时才#import

Item 3: Prefer Literal Syntax over the Equivalent Methods

尽可能使用对象字面量的形式来创建或操作基础对象(NSString、NSNumber、NSArray、NSDictionary等),这种形式不仅使用方便,代码看起来也更清晰。

NSString *someString = @"Effective Objective-C";

NSNumber *someNumber = @1;
NSNumber *intNumber = @10;
NSNumber *floatNumber = @2.5f
NSNumber *doubleNumber = @3.14159;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a'; NSArray *array = @[ object1, object2, object3 ];
NSDictionary *dict = @{ @"name": @"TracyYih",
@"blog": @"http://esoftmobile.com"
}; NSMutableArray *mutableArray = [@[ object1, object2 ] mutableCopy];
mutableArray[2] = object3; NSMutableDictionary *mutableDict = [@{ @"name": @"TracyYih" } mutableCopy];
mutableDict[@"age"] = @25;

Item 4: Prefer Typed Constants to Preprocessor #define

尽量用类型化的常量来代替使用#define定义的常量。

//In head file.
extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
extern NSString *const EOCLoginManagerDidLoginNotification; //In implmentation file.
const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;
NSString *const EOCLoginManagerDidLoginNotification = @"EOCLoginManagerDidLoginNotification";

Item 5: Use Enumerations for States, Options, and Status Codes

使用枚举来表示各种状态,选项。其中申明枚举类型推荐使用Apple的NS_ENUMNS_OPTIONS

typedef NS_ENUM(NSUInteger, EOCConnectionState) {
EOCConnectionStateDisconnected,
EOCConnectionStateConnecting,
EOCConnectionStateConnected,
}; switch (_currentState) {
case EOCConnectionStateDisconnected:
break;
case EOCConnectionStateConnecting:
break;
case EOCConnectionStateConnected:
break;
}

使用枚举表示状态使代码的可读性大大增强,对比下面的代码你就知道了:

switch (_currentState) {
case 0:
break;
case 1:
break;
case 2:
break;
}

Chapter 2: Objects, Messaging, and the Runtime

Item 6: Understand Properties

《Ry’s Objective-C Tutorial》# Properties

Item 7: Access Instance Variables Primarily Directly When Accessing Them Internally

Item 8: Understand Object Equality

在自定义类中,我们可以通过实现-(BOOL)isEqual:(id)object;-(NSUInteger)hash;两个方法来对该类的对象进行比较。

Item 9: Use the Class Cluster Pattern to Hide Implementation Detail

必要时通过工厂模式来隐藏一些实现的细节。

@implementation Employee
+ (Employee *)employeeWithType:(EmployeeType)type {
switch (type) {
case EmployeeTypeDeveloper:
return [[EmployeeDeveloper alloc] init];
break;
case EmployeeTypeDesigner:
return [[EmployeeDesigner alloc] init];
break;
case EmployeeTypeFinance:
return [[EmployeeFinance alloc] init];
break;
}
}
@end

Item 10: Use Associated Objects to Attach Custom Data to Existing Classes

代码内容与该主题看不出来关联,先说主题吧,应该是必要时使用objc_setAssociatedObjectobjc_getAssociatedObject两个方法将一些自定义的数据与已有的类相关联,通常在分类(Category)中添加属性时会用到,后面会涉及到。

代码部分是可以通过函数指针来延迟函数的绑定。

void printHello() {
printf("Hello, world!\n");
}
void printGoodbye() {
printf("Goodbye, world!\n");
} void doTheThing(int type) {
void (*fnc)(); //here.
if (type == 0) {
fnc = printHello;
} else {
fnc = printGoodbye;
}
fnc();
return 0;
}

Item 11: Understand the Role of objc_msgSend

该主题没有对应代码,详见 《Objective-C Runtime Programming Guide》

Item 12: Understand Message Forwarding

必要时我们可以通过实现+ (BOOL)resolveClassMethod:(SEL)sel+ (BOOL)resolveInstanceMethod:(SEL)sel方法来动态的为选择器(selector)提供对应的实现(implementation)。

+ (BOOL)resolveInstanceMethod:(SEL)selector {
NSString *selectorString = NSStringFromSelector(selector);
if (/* selector is from a @dynamic property */) {
if ([selectorString hasPrefix:@"set"]) {
class_addMethod(self, selector, (IMP)autoDictionarySetter, "v@:@");
} else {
class_addMethod(self, selector, (IMP)autoDictionaryGetter, "@@:");
}
return YES;
}
return [super resolveInstanceMethod:selector];
}

Item 13: Consider Method Swizzling to Debug Opaque Methods

我们可以通过runtime提供的method_exchangeImplementations方法来交换两个方法的实现。

// Exchanging methods
Method originalMethod =
class_getInstanceMethod([NSString class],
@selector(lowercaseString));
Method swappedMethod =
class_getInstanceMethod([NSString class],
@selector(uppercaseString));
method_exchangeImplementations(originalMethod, swappedMethod);

这里例子比较有意思,实现了以上代码后,你再使用-(NSString *)lowercaseString;-(NSString *)uppercaseString;时得到的结果和你预期的相反。

Item 14: Understand What a Class Object Is

借助于Objective-C强大的runtime系统,我们可以在代码中判断一个对象是属于什么类型。
isMemberOfClass:判断一个对象是否为某类的实例。
isKindOfClass:判断一个对象是否为某类或该类的子类的实例。

// Class hierarchy checking
NSMutableDictionary *dict = [NSMutableDictionary new];
[dict isMemberOfClass:[NSDictionary class]]; ///< NO
[dict isMemberOfClass:[NSMutableDictionary class]]; ///< YES
[dict isKindOfClass:[NSDictionary class]]; ///< YES
[dict isKindOfClass:[NSArray class]]; ///< NO

Chapter 3: Interface and API Design

Item 15: Use Prefix Names to Avoid Namespace Clashes

在类名(Class)、协议名(Protocal)、分类名(Category)等加上自己的前缀避免与其他库或代码发生命名冲突。

Item 16: Have a Designated Initializer

初始化方法是一个类的入口,所以我们需要精心的设计(其实每个方法都得用心设计),我个人习惯初始化方法中一般不会超过两个参数,尽量让初始化方法更简单,同时我们也需要照顾到继承来的初始化方法:-(id)init;-(id)initWithCode:

// Designated initialiser
- (id)initWithWidth:(float)width
andHeight:(float)height
{
if ((self = [super init])) {
_width = width;
_height = height;
}
return self;
} // Super-class’s designated initialiser
- (id)init {
return [self initWithWidth:5.0f andHeight:10.0f];
} // Initialiser from NSCoding
- (id)initWithCoder:(NSCoder*)decoder {
// Call through to super’s designated initialiser
if ((self = [super init])) {
_width = [decoder decodeFloatForKey:@"width"];
_height = [decoder decodeFloatForKey:@"height"];
}
return self;
}

Item 17: Implement the description Method

我们可以在自己的类中实现description方法,返回关于该对象关键信息,这样在打印(Log)该对象时可以看到更多信息,否则默认就是该对象的类名和地址。

@implementation EOCPerson
...
// Description method for EOCPerson
- (NSString*)description {
return [NSString stringWithFormat:@"<%@: %p, \"%@ %@\">",
[self class], self, _firstName, _lastName];
}
...
@end

Item 18: Prefer Immutable Objects

很多情况下我们申明一个属性只是为了让外部能够获取一些信息(get),并不需要对这些信息作修改(set),所以这种情况下最好不要让外部能够修改,我们可以在申明该属性时加上readonly
或者我们还一可以在实现文件中申明“私有”的成员变量,并开放一个方法来获取该变量的一些信息。

Item 19: Use Clear and Consistent Naming

该部分应该讲的Objective-C编码规范,这里推荐Apple的《Coding Guidelines for Cocoa》

Item 20: Prefix Private Method Names

该部分建议给“私有方法”(只在当前类的实现文件中使用的方法)加上前缀以便和其他方法区分开,这里建议的命名形式为:- (void)_privateMethod;,即加上下杠符_

Item 21: Understand the Objective-C Error Model

在Objective-C中,错误处理可以有两种形式:NSException 和 NSError 。

// Throwing exception
id someResource = …;
if ( /* check for error */ ) {
@throw [NSException exceptionWithName:@"ExceptionName"
reason:@"There was an error"
userInfo:nil];
}
[someResource doSomething];
[someResource release];
// Returning the error
- (BOOL)doSomethingError:(NSError**)error {
// Do something NSError *returnError = nil;
if (/* there was an error */) {
if (error) {
*error = [NSError errorWithDomain:domain
code:code
userInfo:userInfo];
}
return YES;
} else {
return NO;
}
}

其实在Objective-C中后一种更常见,我们可以结合前面提到的使用枚举类表示一些错误码类型。

typedef NS_ENUM(NSUInteger, EOCError) {
EOCErrorUnknown = −1,
EOCErrorInternalInconsistency = 100,
EOCErrorGeneralFault = 105,
EOCErrorBadInput = 500,
};

Item 22: Understand the NSCopying Protocol

我们知道大部分系统的类(UI & NS)可以调用-(id)copy;方法来获得该对象的一份拷贝,如果是自定义的类我们也想使用该方法,必须遵循NSCopying协议,并实现-(id)copyWithZone:(NSZone *)zone);方法。

//Support the NSCopying protocol.
@interface EOCPerson : NSObject <NSCopying>
@end @implementation EOCPerson
// NSCopying implementation
- (id)copyWithZone:(NSZone*)zone {
Person *copy = [[[self class] allocWithZone:zone]
initWithFirstName:_firstName
andLastName:_lastName];
return copy;
}
@end

我们也可以在该方法中控制是深拷贝还是浅拷贝,区别就是是否将当前对象的所有信息(所有成员变量)赋给拷贝后的对象。

Chapter 4: Protocols and Categories

Item 23: Use Delegate and Data Source Protocols for Interobject Communication

《Ry’s Objective-C Tutorial》# Protocols

Item 24: Use Categories to Break Class Implementations into Manageable Segments

当我们要实现一个功能丰富的类时,我们可以使用分类(Category)将该类分割成相对独立一些的块,这样代码结构会比所有东西都放在一起实现要清晰的多。

//RenderObject.h
@class CXMLNode;
@interface RenderObject : NSObject
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, weak, readonly) CXMLNode *node;
@property (nonatomic, strong, readonly) UIView *view;
@property (nonatomic, strong, readonly) NSDictionary *style;
- (instancetype)initWithNode:(CXMLNode *)node style:(NSDictionary *)style;
//...
@end //RenderObject+RenderTree.h
@interface RenderObject (RenderTree)
@property (nonatomic, weak, readonly) RenderObject *parent;
@property (nonatomic, weak, readonly) RenderObject *firstChild;
@property (nonatomic, weak, readonly) RenderObject *lastChild;
@property (nonatomic, weak, readonly) RenderObject *nextSibling;
@property (nonatomic, weak, readonly) RenderObject *previousSibling;
@end //RenderObject+Layout.h
@interface RenderObject (Layout)
- (void)layout;
- (void)loadView;
- (void)paint;
//...
@end

以上代码并非该书所附代码,为笔者开发的一商业浏览器项目代码。

Item 25: Always Prefix Category Names on Third-Party Classes

这个感觉与之前(Item 15)内容相似,给自己创建的所有分类(不管是基于Cocoa类还是第三方类)加上自己的前缀。

// Namespacing the category
@interface NSString (ABC_HTTP) // Encode a string with URL encoding
- (NSString*)abc_urlEncodedString; // Decode a URL encoded string
- (NSString*)abc_urlDecodedString; @end

Item 26: Avoid Properties in Categories

Objective-C分类中是不允许增加成员变量的(Instance variables may not be placed in categories),我们可以通过运行时函数objc_setAssociatedObjectobjc_getAssociatedObject 来让分类支持保存和获取一些数据,从而支持属性。

//EOCPerson+FriendShip.h
@interface EOCPerson (FriendShip)
@property (nonatomic, strong) NSArray *friends;
@end //EOCPerson+FriendShip.m
static const char* kFriendsPropertyKey = "kFriendsPropertyKey";
@implementation EOCPerson (Friendship)
- (NSArray*)friends {
return objc_getAssociatedObject(self, kFriendsPropertyKey);
} - (void)setFriends:(NSArray*)friends {
objc_setAssociatedObject(self, kFriendsPropertyKey, friends, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

Item 27: Use the Class-Continuation Category to Hide Implementation Detail

我们可以在实现文件中利用拓展(Class Extension)将不需要外界了解的成员变量移到拓展中,也就是所有我们应该在头文件中申明为@private的成员变量都可以移到拓展中,这样能够保证头文件只出现外界关心的东西。

//EOCClass.h
@class EOCSuperSecretClass @interface EOCClass : NSObject {
@private
EOCSuperSecretClass *_secretInstance;
}
@end

其中_secretInstance既不会被子类继承,也不会在类外被访问,就不需要留在头文件中了。

//EOCClass.h
@interface EOCClass : NSObject
@end // EOCClass.m
#import "EOCClass.h"
#import "EOCSuperSecretClass.h" @interface EOCClass () {
EOCSuperSecretClass *_secretInstance;
}
@end @implementation EOCClass
// Methods here
@end

在新版本编译器中,实现(@implmentation)中也支持申明成员变量了,所以我们还可以这样写:

@implementation EOCClass {
EOCSuperSecretClass *_secretInstance;
}
// Methods here
@end

Item 28: Use a Protocol to Provide Anonymous Objects

我们可以通过协议来提供匿名对象来调用一些方法或获取一些信息。

// Database connection protocol
@protocol EOCDatabaseConnection
- (void)connect;
- (void)disconnect;
- (BOOL)isConnected;
- (NSArray*)performQuery:(NSString*)query;
@end // Database manager class
#import <Foundation/Foundation.h> @protocol EOCDatabaseConnection; @interface EOCDatabaseManager : NSObject
+ (id)sharedInstance;
- (id<EOCDatabaseConnection>)connectionWithIdentifier:(NSString*)identifier;
@end

这种用法在CoreData中也可以遇到:

// Fetched results controller with section info
NSFetchedResultsController *controller = /* some controller */;
NSUInteger section = /* section index to query */; NSArray *sections = controller.sections;
id <NSFetchedResultsSectionInfo> sectionInfo = sections[section];
NSUInteger numberOfObjects = sectionInfo.numberOfObjects;

Chapter 5: Memory Management

Item 29: Understand Reference Counting

《Ry’s Objective-C Tutorial》#Memory Management

Item 30: Use ARC to Make Reference Counting Easier

《Ry’s Objective-C Tutorial》#Memory Management

Item 31: Release References and Clean Up Observation State Only in dealloc

在ARC模式下,dealloc方法中一般只应该出现两种操作:释放非Cocoa对象和移除观察者。

// Releasing CF objects and removing observer in `dealloc'
- (void)dealloc {
CFRelease(coreFoundationObject);
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

Item 32: Beware of Memory Management with Exception-Safe Code

在MRR(Manual Retain Release)下,需要特别留意异常情况下的内存管理问题。

// @try/@catch block under manual reference counting
@try {
EOCSomeClass *object = [[EOCSomeClass alloc] init];
[object doSomethingThatMayThrow];
[object release];
}
@catch (...) {
NSLog(@"Whoops, there was an error. Oh well, it wasn’t important.");
}
// Fixing the potential leak
EOCSomeClass *object;
@try {
object = [[EOCSomeClass alloc] init];
[object doSomethingThatMayThrow];
}
@catch (...) {
NSLog(@"Whoops, there was an error. Oh well, it wasn’t important.");
}
@finally {
[object release];
}

其实同样需要注意的还有在switch-case或if-else条件下,避免return前必要的对象没释放问题。

Item 33: Use Weak References to Avoid Retain Cycles

Item 34: Use Autorelease Pool Blocks to Reduce High-Memory Waterline

// Reducing high memory waterline with appropriately places @autoreleasepool
NSArray *databaseRecords = …;
NSMutableArray *people = [NSMutableArray new];
for (NSDictionary *record in databaseRecords) {
@autoreleasepool {
EOCPerson *person = [[EOCPerson alloc] initWithRecord:record];
[people addObject:person];
}
}

Item 35: Use Zombies to Help Debug Memory-Management Problems

Item 36: Avoid Using retainCount

// Never do this
while ([object retainCount]) {
[object release];
}

Effective Objective-C [上]的更多相关文章

  1. 【硅谷问道】Chris Lattner 访谈录(上)

    [硅谷问道]Chris Lattner 访谈录(上) 话题 Chris Lattner 是谁? Xcode 的编译器 LLVM 背后有怎样的故事? Swift 诞生的前世今生,封闭的苹果为何要拥抱开源 ...

  2. iOS 学习资源

    这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的 ...

  3. iOS学习资料整理

    视频教程(英文) 视频 简介 Developing iOS 7 Apps for iPhone and iPad 斯坦福开放教程之一, 课程主要讲解了一些 iOS 开发工具和 API 以及 iOS S ...

  4. iOS 学习

    iOS 学习资料 (适合初学者) 本文资料来源于GitHub 一.视频教程(英文) Developing iOS 7 Apps for iPhone and iPad斯坦福开放教程之一, 课程主要讲解 ...

  5. iOS 学习资料汇总

    (适合初学者入门) 本文资料来源于GitHub 一.视频教程(英文) Developing iOS 7 Apps for iPhone and iPad斯坦福开放教程之一, 课程主要讲解了一些 iOS ...

  6. ios书籍推荐

    1.Objective-C Programming  内容不多, 却都是精华, 有了一点 C 语言基础可以快速阅读此书, 大概一天时间就可以看完, 看完后对 iOS 开发能够有个基本的印象. 2.iO ...

  7. iOS Learning

    转载自:http://www.cocoachina.com/ios/20150111/10894.html iOS 学习资料整理 2015-01-11 20:20 编辑: suiling 分类:iOS ...

  8. BlocksKit的使用

    一.引言 众所周知Block已被广泛用于iOS编程.它们通常被用作可并发执行的逻辑单元的封装,或者作为事件触发的回调.Block比传统回调函数有2点优势: 允许在调用点上下文书写执行逻辑,不用分离函数 ...

  9. 【iOS】关联属性存取数据

    有时候我们需要在现有的类存放一些额外的信息,通常的做法是继承一个子类,然后定义新增加的属性,然而如果我们为每个需要的类都添加一个类显得太麻烦了,objc提供了一个关联属性的特性,可以给一个对象关联一个 ...

  10. malloc free 和new delete区别

    从网上看的学习之 1. malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符,与"+“.”-“.”*“.”/“有一样的地位. 2. new/delete是 ...

随机推荐

  1. 2018.7.27 wireless charger TX evaluation kit based on STWBC-EP

    1 introduced 我们需要设计一个无线充电方案: 功能需求:通用的无线充电平台 参数要求:8-10W step1: 找寻资料  http://www.ti.com/sitesearch/doc ...

  2. 在OpenCV2.2后的版本中没有CvvImage类的解决方法(及出现错误:IntelliSense: 未定义标识符 "CvvImage" )

    首先在你的解决方案资源管理器中的头文件和源文件下分别添加 CvvImage.cpp 如下图: view类头上加个#include "CvvImage.h"  头文件,应该就可以解决 ...

  3. Number Sequence (KMP的应用)

    个人心得:朴素代码绝对超时,所以要用到KMP算法,特意了解了,还是比较抽象,要多体会 Given two sequences of numbers : a11, a22, ...... , aNN, ...

  4. python导入csv文件时,出现SyntaxError

    背景 np.loadtxt()用于从文本加载数据. 文本文件中的每一行必须含有相同的数据. *** loadtxt(fname, dtype=<class 'float'>, commen ...

  5. 运行flask程序

    Command Line Interface Installing Flask installs the flask script, a Click command line interface, i ...

  6. C# Chat曲线图,在发布之后出现错误 Invalid temp directory in chart handler configuration c:\TempImageFiles\

    First error message: Invalid temp directory in chart handler configuration c:\TempImageFiles\ Soluti ...

  7. 批量删除osd的shell脚本

    cluster环境: # cat /etc/redhat-release CentOS Linux release 7.3.1611 (Core) # ceph -v ceph version 12. ...

  8. 未在本地计算机上注册 Microsoft.ACE.OLEDB.12.0 提供程序

    Visual Studio 8使用了Access数据库,provider选择了ACE.OLEDB,但是运行时出现了错误,提示未在本地计算机上注册"Microsoft.ACE.OLEDB.12 ...

  9. xcode修改横屏

    1.修改工程属性 2.修改info.plist文件

  10. [摘]Android逆向分析常用网站

    androidterm:   Android Terminal Emulator   http://code.google.com/p/androidterm/   droidbox:   Andro ...