第二条少用#import,多用@class,向前声明,将引入头文件的时机尽量延后,只有在需要时才引入。可以减少编译时间和防止循环引用。
.h文件用@class MyView;
# import <UIKit/UIKit.h>
@classMyView;
@interfaceViewController : UIViewController
- (void)sendMyView:(MyView*)mine;
@end
.m文件用#import"MyView.h",因为需要MyView的方法,否则会报错
#import "ViewController.h"
#import "MyView.h"
@interface ViewController()
@end
@implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
MyView *myview = [[MyView alloc]init];
myview.title =@"这是一个view";
[self sendMyView:myview];
}
- (void)sendMyView:(MyView *)mine
{
}
第三条多用字面量语法,以下两种字符串的初始化方法是等价的,但是第二种会更加简洁,本书建议用第二种方法,我觉得根据实际情况来操作,如果是数组或者是字典的初始化我会选择用第一种方法,因为我不希望程序在遇到异常值的时候奔溃。
字符串初始化方法
NSString *str = [NSString stringWithFormat:@"abc"];
NSString *str = @"abc";
类似的初始化方法中还有数组字典等,但是元素不能为nil,否则会奔溃。
NSArray *array = @[@"小明",@"小东",@"小红"];
NSDictionary *dictionary = @{
@"firstName":@"wu",
@"lastName":@"qingdao",
@"age":@10
};
NSString *str = nil;
NSArray *array = @[@"小明",@"小东",str];(会奔溃)
NSArray *array = [NSArray arrayWithObjects:@"小明",@"小东",str,nil];(不会奔溃)
第四条多用类型常量,少用#define
如果只是在本类中使用,那么就在.m文件中定义即可,而且一般以小写字母“k”开头,不管是类型常量还是宏。
如果用宏定义,那么有可能被替换,比如在一个类中定义kAnimationDuration为0.3,另一个类中定义kAnimationDuration为0.4
#define kAnimationDuration 0.3
类型常量kAnimationDuration附带类型信息,并且试图修改kAnimationDuration的值的时候会报错
static const NSTimeIntervalkAnimationDuration = 0.3;
如果打算把这个常量公开,也就是其他类里面也能用到这个常量,那就应该如下定义(extern关键字),请注意“对象常量”(如NSString)和“一般常量”(如NSInterger)写法上的区分。
#import <UIKit/UIKit.h>
extern const NSTimeInterval XJXAnimationDuration;
extern NSString*constXJXMyName;
@interface MyView : UIView
@end
#import "MyView.h"
@implementation MyView
NSTimeInterval const XJXAnimationDuration=0.3;
NSString*const XJXMyName = @"xiao";
@end
第五条 二进制枚举可以减少定义的元素
//枚举值 二进制 十进制
NOONE =0 //没人 00000000 0
WORKER =1<<0, //工人 00000001 1
DESINGER =1<<1, //设计师 00000010 2
MANAGER =WORKER | DESINGER,//经理 00000011 3
FINANCIAL =1<<2 //财务 00000100 4
BOSS =FINANCIAL | MANAFGER,//老板 00000111 7
任意两个二进制数,两两按位"或"后得到的数据都是唯一的,比如 WORKER | DESINGER.最后得到的二进制数是00000011。(NOONE除外,因为它与任何数按位"或"后都是,那个数本身)
所以二进制来表示枚举值的优势是用更少的数据,可以表达更多的状态。所以我们也可以这样定义:
//枚举值 二进制 十进制
NOONE =0 //没人 00000000 0
WORKER =1<<0, //工人 00000001 1
DESINGER =1<<1, //设计师 00000010 2
FINANCIAL =1<<2 //财务 00000100 4
那如果 return WORKER | DESINGER; 就表示这个人是经理
如果 return WORKER | DESINGER| FINANCIAL; 就表示这个人是老板。
例子:
#import "ViewController.h"
typedef NS_ENUM(NSUInteger,PostStyle){
//枚举值 二进制 十进制
NOONE =0, //没人 00000000 0
WORKER =1<<0, //工人 00000001 1
DESINGER =1<<1, //设计师 00000010 2
FINANCIAL =1<<2 //财务 00000100 4
};
@interfaceViewController (){
PostStyle style;
}
@end
@implementationViewController
- (PostStyle)Boss
{
return WORKER | DESINGER| FINANCIAL;
}
- (PostStyle)manager
{
return WORKER | DESINGER;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"状态码:老板:%ld,经理:%ld",(unsignedlong)[self Boss],(unsigned long)[selfmanager]);
}
控制台打印:
**2016-06-1112:55:15.186 52Effective_Objc[1805:434069] ****状态码:老板:****7,****经理:****3****
第七条 对象内部读取数据时,应该通过实例变量(_name)来读取,写入数据用属性来写
懒加载时必须使用实例变量(_name),用self.name会造成死循环
懒加载的优点(其实就是重写 了get方法)
1不需将对象的实例化写到viewDidLoad,可以简化代码,增强代码的可读性
2对象的实例化在getter方法中,各司其职,降低耦合性
3对系统的内存占用率会减小
#import "XJDropdownMenu.h"
@interface XJDropdownMenu()
@property(nonatomic,weak)UIImageView *containView;
@end
@implementation XJDropdownMenu
想调用懒加载,就需要用到get方法:self.containView
-(UIImageView *)containView
{
if (!_containView) { //这里不能用self.containView
UIImageView *grayBackImageView = [[UIImageView alloc]init];
_containView = grayBackImageView;
}
return _containView;
}
调用用懒加载
[self.view bringSubviewToFront:self.containView];
第九条 以“类族模式”隐藏实现细节。可以隐藏“抽象基类”的实现细节。
假如有一个雇员的类(XJEmployee)。它的初始化方法
+ (XJEmployee*)employeeWithType:(EmployeeType)type;
由type来决定返回的类型。一般来说我们会这样写:
typedef NS_ENUM(NSUInteger,EmployeeType){
Developer,
Desiginer,
Finance
};
+ (XJEmployee*)employeeWithType:(EmployeeType)type{
XJEmployee *employee =[[self alloc]init];
switch (type) {
case Desiginer:
//符合Desiginer的一些属性赋值
break;
case Developer:
//符合Developer的一些属性赋值
break;
case Finance:
//符合Finance的一些属性赋值
break;
default:
break;
}
return employee;
}
如果使用族类模式,那么用户无需知道实现细节,这些细节交给XJEmployee的基类去实现。
声明文件
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger,EmployeeType){
Developer,
Desiginer,
Finance
};
@interfaceXJEmployee : NSObject
@property(nonatomic,copy)NSString*name;
@property(nonatomic,assign)NSUIntegersalary;
+ (XJEmployee*)employeeWithType:(EmployeeType)type;
- (void)doAdaysWork;
@end
实现文件
#import "XJEmployee.h"
#import "XJDesigner.h"
#import "XJDeveloper.h"
#import "XJFinance.h"
@implementation XJEmployee
+ (XJEmployee*)employeeWithType:(EmployeeType)type{
switch (type) {
case Desiginer:
return[XJDesigner new];
break;
case Developer:
return[XJDeveloper new];
break;
case Finance:
return[XJFinance new];
break;
default:
break;
}
}
- (void)doAdaysWork{
//子类复写这个方法。
}
其中的一个子类XJEmployee 声明文件
#import "XJEmployee.h"
@interface XJDeveloper : XJEmployee
@end
实现文件
#import "XJDeveloper.h"
@implementation XJDeveloper
- (void)doAdaysWork
{
NSLog(@"XJDeveloper");
}
@end
另:本书提到UIButton也是使用了族类模式,我验证了一下,发现与书中所说的不符。
UIButton*bun = [UIButton buttonWithType:UIButtonTypeContactAdd];
NSLog(@"UIButtonTypeContactAdd-->%@",bun);
if([bun isMemberOfClass:[UIButton class]]){
NSLog(@"bunisMemberOfClass_UIButton");
}
if ([bun isKindOfClass:[UIButton class]]){
NSLog(@"bunisKindOfClass_UIButton");
}
//控制台打印数据:
**UIButtonTypeContactAdd--><UIButton:0x7fe710d69e60; frame = (0 0; 22 22); opaque = NO; layer = <CALayer:0x7fe710d6a340>>
**
**2016-06-0615:11:28.526 ****关联对象用法****[2148:188055]bun isMemberOfClass_UIButton
**
**2016-06-06 15:11:28.529 ****关联对象用法****[2148:188055] bun isKindOfClass_UIButton
-isMemberOfClass: 是 NSObject 的方法,用以确定某个 NSObject 对象是否是某个类的成员。与之相似的为 -isKindOfClass:,可以用以确定某个对象是否是某个类或其子类的成员。也就是说bun是UIButton的成员。
验证一下XJEmployee
XJEmployee*ployee = [XJEmployee employeeWithType:Developer];
NSLog(@"Developer-->%@");
[XJEmployee employeeWithType:Developer]);
if ([ployee isKindOfClass:[XJEmployee class]]){
NSLog(@"ployeeisKindOfClass_XJEmployee");
}
if ([ployee isMemberOfClass:[XJEmployee class]]){
NSLog(@"ployeeisMemberOfClass_XJEmployee");
}
控制台打印数据:
**2016-06-0615:33:38.171 ****关联对象用法****[2230:195652]Developer--><XJDeveloper: 0x7fd8b250e300>
**
**2016-06-06 15:33:38.172 ****关联对象用法****[2230:195652] ployee isKindOfClass_XJEmployee
只打印了ployeeisKindOfClass_XJEmployee.说明ployee不是XJEmployee的成员,而是XJEmployee子类的成员。