1.多用类型常量,少用#define预处理指令。
如
static const NSTimeInterval kAnimationDuration = 0.3
使用类型常量定义常量含有类型信息,如果常量值类型不一致会有警告信息。
2.在头文件中使用extern声明全局常量,并在相关实现文件中定义其值。
如
EOCAnimatedView.h
extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
EOCAnimatedView.m
const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;
3.用枚举表示状态、选项、状态码
1.如果选项表示为枚举类型,可以有多个选项可以同时使用,那么就将个选项值定义为2的幂,以便可以按位或操作将其组合。
如
typedef NS_ OPTIONS(NSUInteger,EOCPermittedDirection){
EOCPermittedDirectionUp = 1<<0,
EOCPermittedDirectionDown = 1<<1,
EOCPermittedDirectionLeft = 1<<2,
EOCPermittedDirectionRight = 1<<3,
};
2.用NS_ENUM,NS_OPTIONS宏来定义枚举类型,并指明起底层数据类型,可以保证枚举是用开发者选择的底层数据类型来实现,而不会采用编译器所选类型。
4.在对象内部尽量直接访问实例变量
直接访问实例变量的原因:
*由于不经过Objective-C的“方法派发”步骤,直接访问实例变量速度比较快。
*直接访问实例变量时,不会调用“设置方法”绕过了相关属性的“内存管理语意”比如在ARC下,直接访问一copy属性,那么不会考呗该属性,只会保留新值释放旧值。
*如果直接访问实例变量,不会触发“键值观察KVO”通知。
1.在对象内部读取数据时,应该直接通过实例变量读取,写入数据时,通过属性来写。
2.在初始化方法及dealloc方法中,应该直接通过实例变量读写数据。
3.在懒加载中必须通过属性来读取数据。
5.以“类族模式”隐藏实现细节
这种模式最大的好处就是,可以隐藏抽象基类背后的复杂细节,使用者只需调用基类简单的方法就可以返回不同的子类实例。
如UIButton的类方法
+ (UIButton *)buttonWithType:(UIButtonType)type;
自定义个类族
首先定义抽象基类
typedef NS_ENUM(NSUInteger, EOCEmployeeType) {
EOCEmployeeTypeDeveloper, EOCEmployeeTypeDesigner, EOCEmployeeTypeFinance,
}
@interface EOCEmployee : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSUInteger salary;
@end
@implementation EOCEmployee
+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type {
switch (type) {
case EOCEmployeeTypeDeveloper:
return [EOCEmployeeDeveloper new];
break;
case EOCEmployeeTypeDesigner:
return [EOCEmployeeDesigner new];
break;
case EOCEmployeeTypeFinance:
return [EOCEmployeeTypeFinance new];
break;
}
- (void)doADaysWork{
}
每个子类都是从基类继承而来。
@interface EOCEmployeeDeveloper : EOCEmployee
@end
@implementation EOCEmployeeDeveloper
- (void)doADaysWork{
[super doADaysWork];
NSLog(@"%@",[[self class] description]);
}
@end
6.消息转发机制
在“消息传递机制”中,当对象接收到无法解析的消息时,就会启动“消息转发”机制
1.动态方法解析
对象在收到无法解析的消息后,首先会调用所属类的
+ (BOOL)resolveInstanceMethod:(SEL)selector;
或者
+ (BOOL)resolveClassMethod:(SEL)selector;
在这里可以新增一个处理该"选择子"的方法,如
#import <Foundation/Foundation.h>
@interface EOCAutoDictionary : NSObject
@property (nonatomic, copy) NSString *string;
@property (nonatomic, strong) NSNumber *number;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) id opaqueObject;
@end
#import "EOCAutoDictionary.h"
#import <objc/runtime.h>
@interface EOCAutoDictionary()
@property (nonatomic, strong) NSmutableDictionary *backingStore;
@end
@implementation EOCAutoDictionary
@dynamic string, number, date, opaqueObject;
- (id)init{
if (self = [super init]){
_backingStore = [NSMuatableDictionary new];
}
return self;
}
+ (BOOL)resolveInstanceMethod:(SEL)selector{
NSString *selectorString = NSStringFromSelector(selector);
if ([selectorString hsaPrefix:@"set"]){
class_addMethod(self, selector,(IMP)autoDictionarySetter,"v@:@)"
} else {
class_addMethod(self, selector,(IMP)autoDictionaryGetter,"@@:");
}
return YES
}
// Getter方法实现
id autoDictionaryGetter(id self, SEL _cmd){
EOCAutoDictionary *typeSelf = (EOCAutoDictionary *)self;
NSMutableDictionary *backingStore = typedSelf.backingStore;
NSString *key = NSStringFromSelector(_cmd);
return [backingStore objectForKey:key];
}
// Setter方法实现
id autoDictionarySetter(id self, SEL _cmd, id value) {
EOCAutoDictionary *typeSelf = (EOCAutoDictionary *)self;
NSMutableDictionary *backingStore = typedSelf.backingStore;
NSString *selectorString = NSStringFromSelector(_cmd);
NSMutableString *key = [selectorString mutableCopy];
// 删除结尾的:
[key deleteCharactersInRange:NSMakeRange(key.length - 1,1)];
// 删除头部“set”
[key deleteCharactersInRange:NSMakeRange(0,3)];
}
NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString];
[key replaceCharactersInRange:NSMakeRange(0,1) withString:lowercaseFirstChar];
if (value) {
[backingStore setObject:value forKey:key];
} else {
[backingStore removeObjectForKey:key];
}
}
@end
// 使用
EOCAutoDictionary *dict = [EOCAutoDictionary new];
dict.date = [NSDate dateWithTimeIntervalSince1970:475328000];
NSLog(@"dict.date = %@",dict.date);
// 输出: dict.date = 1985-01-24 00:00:00 +0000
2.备援接收者
如果没有实现“动态方法方法解析”则会走这一步,能不能把消息转给其他接收者来处理,对应方法为
- (id)forwardingTargetForSelector:(SEL)selector
如果找到其他对象,则将其返回,没有找到,则返回nil。
注意:我们无法操作经由这一步的转发消息。
3.完整的消息转发
首先会创建NInvocation对象,把未处理的消息的全部细节都封装该对象中,包括选择子,目标,参数。在触发NSInvocation对象时,“消息派发系统”会吧消息指派给目标对象,此步骤会调用下列方法来转发消息:
- (void)forwardInvocation:(NSInvocation *)invocation;
如果还是没有处理该消息,则会调用“doesNotRecognizeSelector:”抛出异常
7.使用自动释放池降低内存峰值
如
NSArray *databaseRecords = [NSArray Array];
NSMutableArray *people = [NSMutableArray New];
for (NSDictionary *record in databaseRecords) {
@autoreleasepool {
EOCPerson *person = [EOCPerson alloc] initWithRecord:record];
[people addObject:person];
}
}
8.栈块,堆块,全局块
// 由于定义在if和else中的两个块都分配在占栈内存中,离开相应的范围可能会被释放,
void (^block)();
if (some condition) {
block = ^ {
NSLog(@"Block A");
};
} else {
block =^ {
NSLog(@"Block B");
};
}
block();
// 可以给块对象发送Copy消息拷贝到堆中,防止过早释放
void (^block)();
if (some condition) {
block =[ ^ {
NSLog(@"Block A");
} copy];
} else {
block =[^ {
NSLog(@"Block B");
} copy];
}
block();
// 全局块声明在全局内存中,这种快不会捕捉任何状态(比如外围变量等),全局快的拷贝操作是个空操作
void (^block)() =^{
NSLog(@"This is a block");
};
9.多用派发队列,少用同步锁
如果多线程同时执行同一份代码时,我们需要使用锁来实现同步访问,
第一种方法,同步块
- (void)synchronizedMethod{
@synchronized(self) {
// 安全代码
}
}
// 滥用@s'ynchronized(self)则会降低代码效率,因为所有同步块都会彼此抢夺同一个锁,要是有多个属性都这么写的话,每个属性的同步块都要等其他所有同步块执行完毕才能执行,
第二种方法, 使用NSLock
_lock = [[NSLock alloc] init];
- (void)synchronizedmethod{
[_lock lock];
// 安全代码
[_lock unlock];
}
第三种方法,递归锁(NSRecursiveLock)
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
KYS_GLOBAL_QUEUE(^{
static void (^RecursiveBlock)(int);
RecursiveBlock = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"加锁层数 %d", value);
sleep(1);
RecursiveBlock(--value);
}
[lock unlock];
};
RecursiveBlock(3);
});
第二种和第三种也有缺陷,在极端情况下,同步快会导致死锁,效率也不是很高
第四种方法, 使用串行同步队列
把读取和写入操作都安排在同一个队列里,保证数据同步
_syncQueue = dispatch_queue_create("com.effectiveobjective.syncQueue".NULL);
- (NSString *)someString{
_block NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = _someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString *)someString {
dispatch_sync(_syncQueue, ^{
_someString = someString;
});
}
第五中,使用并发队列加栅栏,性能更佳
_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
- (NSString *)someString{
_block NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = _someString;
});
return localSomeStrng;
}
- (void)setSomeString:(NSString *)someString{
dispatch_barrier_async(_syncQueue, ^{
_someString = someString;
});
}
10.打破NSTimer的循环引用
// 创建NSTimer分类,使用弱引用打破循环
#import <Foundation/Foundation.h>
@interface NSTimer (EOCBlocksSupport)
+ (NSTimer *)etc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats;
@end
@implementation NSTimer (EOCBlocksSupport)
+ (NSTimer *)etc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats{
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(etc_blockinvoke:) userInfo:[block copy] repeats:repeats];
}
+ (void)eoc_blockInvoke:(NSTimer *)timer {
void (^block)() = timer.userInfo;
if (block) {
block();
}
}
// 使用
- (void)startPolling {
__weak EOCClass *weakSelf = self;
_pollTimer = [NSTimer eoc_scheduledTimerWithTimeInterval:5.0 block:^{
EOCClass *strongSelf = weakSelf;
[strongSelf p_doPoll];
}
repeats:YES];
}
网友评论