Effective Objective-C 笔记

1、在类的头文件中尽量少引用其他头文件

把引入头文件的时机尽量延后,减少编译时间。

2、尽量使用字面量语法字符、数组、字典、数值

 NSString *str = nil;
NSArray *arr = [NSArray arrayWithObjects:@"1",@"2",str, nil];
NSArray *arr1 = @[@"1",@"d",str];

上面例子中arr1会创建的时候碰到nil的时候崩溃,方便查找错误。arr的创建则不会崩溃。

3、多用类型常量、少用#define预处理指令

例如:我们可能用#define定义一个动画时间常量

#define AnimationDuration 0.3

预处理会替换AnimationDuration为0.3,如果所有引入这个头文件的AnimationDuration都会被替换。
替代方案:

static const NSTimeInterval KAnimationDuration = 0.3;

类型常量的的命名规则:1、如果只在.m中用则在前面加K
2、如果是其他文件引用则通常以类名做为前缀。
利用extern来定义全局常量,如定义一个test通知的名字在.m中

NSString *const VieControllerTestNotification = @"VieControllerTestNotification";

在.h文件中

extern NSString *const VieControllerTestNotification;

3、理解“对象的等同性”

a.==操作符比较的是两个指针本身,而不是所指的对象。如下例子:

    NSString *first = @"first 1";
NSString *second = [NSString stringWithFormat:@"first %d",1];

BOOL equalA = (first == second);
BOOL equalB = [first isEqual:second];
BOOL equalC = [first isEqualToString:second];
equalResult.png

打断点可以看结果,==的比较结果为NO
b.NSObject协议中有两个用于判断等同性的关键方法:

- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;
QQ20170413-0.png

c.例如我们自定义一个Person类的判断等同性的方法

@interface Person : NSObject
/** firstName */
  @property (nonatomic,strong) NSString *firstName;

/** lastName */
@property (nonatomic,strong) NSString *lastName;
@end

等同性方法实现

- (BOOL)isEqualToPerson:(Person*)otherPerson {

if(self == otherPerson) return YES;

if(![_firstName isEqualToString:otherPerson.firstName]) return NO;

if(![_lastName isEqualToString:otherPerson.lastName]) return NO;

return YES;
}


- (BOOL)isEqual:(id)object {


if ([self class]  == [object class]) {
    
    return [self isEqualToPerson:(Person*)object];
}
else{

    return [super isEqual:object];
}

}

- (NSUInteger)hash {

NSUInteger firstHash = [_firstName hash];

NSUInteger lastHash = [_lastName hash];

return firstHash ^ lastHash;
}

4、以“类簇模式”隐藏细节

系统的UIButton就是一个很好的例子,调用基类方法可以创建不同类型的按钮,实际上返回的是对应不同的子类实例。

+ (instancetype)buttonWithType:(UIButtonType)buttonType;

例如我们需要设计一个公司雇员的类,公司的雇员又分很多种其中有工程师和设计师两种。

typedef NS_ENUM(NSUInteger,YJEmployeeType){

YJEmployeeTypeDeveloper,
YJEmployeeTypeDesigener
};
@interface YJEmployee : NSObject

+ (YJEmployee*)employeeWithType:(YJEmployeeType)type;

- (void)doWork;

通过类方法创建不同雇员实例,doWork则交给不能的子类实现。
开发者的doWork实现

@implementation YJDeveloper
- (void)doWork {

NSLog(@"我要写代码");
}
@end

设计师的doWork实现

@implementation YJDesiger
- (void)doWork {

NSLog(@"我要搞设计");
}
@end

5、理解消息转发机制

当调用某个对象或者类未实现的方法时会崩溃,

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[YJMessageForword test]: unrecognized selector sent to instance 0x600000004e70'

下面是消息进行转发的流程图


messageForword.png

如果我们想让调用给未实现的方法不崩溃我们一般在前两步中进行操作。
1、在+ (BOOL)resolveInstanceMethod:(SEL)sel;中为其动态添加一个方法

+ (BOOL)resolveInstanceMethod:(SEL)sel {


return class_addMethod([self class], sel, (IMP)dynamciMethodIMP, "v@:");
}

动态方法的实现如下:

void dynamciMethodIMP (id self,SEL _cmd){

NSLog(@"我是被动态添加的测试方法");
}

2、或者在- (id)forwardingTargetForSelector:(SEL)aSelector中动态添加一个对象并动态添加一个方法

- (id)forwardingTargetForSelector:(SEL)aSelector {


Class testClass = objc_allocateClassPair([NSObject class], "YJTest", 0);

objc_registerClassPair(testClass);

id testObject = [[testClass alloc] init];


class_addMethod(testClass, aSelector, (IMP)dynamciMethodIMP, "v@:");



return testObject;
}

在这里对 class_addMethod只不过最后一个参数说明一下:第一个v-代表返回值为void ,@代表id,:代表SEL。
下图为各种符号代表的类型:

QQ20170413-1.png
QQ20170413-2.png

6、方法调配

  Method originMethod = class_getInstanceMethod([NSString class], @selector(uppercaseString));

Method changeMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));

method_exchangeImplementations(originMethod, changeMethod);

7、重写description

重写description返回有意义的字符串

8、block

1、block的定义:
block用^来表示,后面加{},在括号里面就是实现代码。下面是最简单的块。

 void (^Block)() = ^ {

    NSLog(@"我是一个最简单的块");
};

block的语法类似于函数指针

returen_type (^block_name)(paramters)

下面定义返回值为int,并接受两个int参数

 int (^addBlock)(int a ,int b ) = ^(int a ,int b){

    return a + b;
};

block本身也是对象
2、全局block、栈block、堆block
定义block 的时候,其所占的内存区域是分配在栈中,也就是说block值在定义它的范围内有效。例如下面的代码是不安全的

 void(^testBlock)();
BOOL condition;
if (condition) {
    
    testBlock = ^ {
    
        NSLog(@"Block test is good");
    };
}
else{

    testBlock = ^{
    
      
        NSLog(@"un safe Block");
    };
}

testBlock();

定义在if和else中两个block分配在栈中,离开作用域之后,编译器有可能吧分配给block的内存覆写掉。所以上面的代码运行有时候正确还,有时错误。为解决此问题,可以给block对象发送copy消息,这样就是能把block从栈复制到堆内存中了。

8、线程安全少用同步锁

在OC中,如果多个线程要执行同一份代码,那么有时候会出问题。这种情况,通常使用锁来实现某种同步机制。如下面的代码,是不安全的。

   NSMutableArray *arr = [NSMutableArray array];

for (int i = 0; i<1000; i++) {
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{

            
        [arr addObject:[NSString stringWithFormat:@"%d",i]];

    
    });
    
}

解决办法:
1、 @synchronized

 @synchronized (self) {
            
           [arr addObject:[NSString stringWithFormat:@"%d",i]];
        }

这种写法会根据给定的对象,自动创建一个锁,等等待块中代码执行完毕,锁就释放。但如果滥用@synchronized (self)则会降低代码效率,因为共用同一个锁的那些同步块必须都按顺序执行。
2、NSLock加锁

   NSMutableArray *arr = [NSMutableArray array];


NSLock *lock = [[NSLock alloc] init];
for (int i = 0; i<1000; i++) {
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        [lock lock];
           [arr addObject:[NSString stringWithFormat:@"%d",i]];
        
        [lock unlock];
    });
    
}

3、dispatch_semaphore_t

NSMutableArray *arr = [NSMutableArray array];

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i<1000; i++) {
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{

    
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        [arr addObject:[NSString stringWithFormat:@"%d",i]];
        
        dispatch_semaphore_signal(semaphore);

       

    });
    
}

4、有种简单而高效的办法就是使用“串行同步队列”来代替同步块或锁对象。(这里就不上代码了)
5、并发队列配合栅栏

@interface YJSyncObjct ()

@property dispatch_queue_t syncQueue;

@end

@implementation YJSyncObjct

@synthesize someString = _someString;

- (instancetype)init
{
self = [super init];
if (self) {
    _syncQueue = dispatch_get_global_queue(0, 0);
}
return self;
}


- (NSString*)someString {

__block  NSString *localSomeString;


dispatch_sync(_syncQueue, ^{
   
    localSomeString = _someString;
});

return localSomeString;
}

- (void)setSomeString:(NSString *)someString {

dispatch_barrier_async(_syncQueue, ^{
   
    _someString = someString;
});
}

下图为操作时序图

操作时序.png

9、利用block解决NSTimer循环引用问题

@implementation NSTimer (YJBlocksSuppot)

+ (NSTimer*)yj_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeates:(BOOL)repeates {


return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(yj_blockInvoke:) userInfo:[block copy] repeats:repeates];
}


+ (void)yj_blockInvoke:(NSTimer*)timer {

void(^block)() = timer.userInfo;

if (block) {
    
    block();
}
}
@end

这个是iOS10以前需要写分类,iOS10以后系统已经存在该方法了

QQ20170414-0.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容