OC语言学习路线及重难点
- 关键字
- 数据类型
- 对象存储细节
- Xcode文档安装和自定义代码段
- 动态数据类型id
- 构造方法、类工厂方法
- 类的启动过程(+load、+initialize)
- 内存管理 MRC
- 自动引用计数 ARC
- Category 和 Extension
- Block
- Protocol
- Foundation框架
- Copy
- ……
关键字
// C语言中一共有32个关键字
auto double int struct break else long switch
case enum register typedef char extern return
union const float short unsigned continue for
signed void default goto sizeof volatile do if while static
// OC中新增的关键字
@interface @implementation @end
@public @protected @private @selector
@try @catch @throw @finally
@protocol @optional @required @class
@property @synthesize @dynamic
BOOL Class SEL YES NO id self
super nil atomic nonatomic retain assign copy block ...
数据类型
-
C语言数据类型
-
OC中数据类型
对象存储细节
isa指针
- 每一个对象都包含一个
isa
指针,这个指针指向当前对象所属的类 - 调用对象方法时,此时对象会顺着内部的
isa
指针找到存储于类中的方法并执行 -
isa
是对象中的隐藏指针,指向创建这个对象的类 - 通过
isa
指针我们可以在运行的时候知道当前对象是属于那个Class(类)的
Xcode文档安装和自定义代码段
- 掌握在线及离线安装文档步骤;及培养阅读一手文档的习惯
- 自定义代码:导入代码片段
将下载好的代码片段拷贝到:/Users/litongde/Library/Developer/Xcode/UserData/CodeSnippets
下
动态数据类型id
-
id == NSObject *
万能指针
NSobject *
是一个静态数据类型
id
是一个动态数据类型 - 通过静态数据类型定义变量,不能调用子类特有方法
- 通过动态数据类型定义变量,可以调用子类特有方法
- 通过动态数据类型定义变量,可以调用私有方法
- 弊端:由于动态数据类型可以调用任意方法,所以可能会调用不属于自己的方法,导致运行时报错
构造方法、类工厂方法
// 构造方法
- (instancetype)init {
if (self = [super init]) {
// Initialize self.
}
return self;
}
// 类工厂方法
+ (instancetype)类名称
{
// 谁调用这个方法,self就代表谁
// 注意:写类方法创建初始化对象,写self不要直接写类名
return [[self alloc] init];
}
类的启动过程(+load、+initialize)
+load方法
- 只要程序启动就会将所有类的代码加载到内存中,放到代码区
-
load
方法会在当前类被加载到内存的时候调用,有且仅会调用一次 - 如果存在继承关系,会先调用父类的
load
方法,再调用子类的load
方法,跟initialize
一样
+initialize方法
- 当前类第一次被使用的时候就会调用(创建类对象的时候)
-
initialize
方法在整个程序的运行过程中只会被调用一次 -
initialize
用于对某一个类进行一次性初始化
内存管理 MRC
// set方法内存管理 例
- (void)setRoom:(Room *)room
{
// 避免过度释放
if (room != _room) {
// 对当前正在使用的房间(旧房间)做一次release
[_room release];
// 对新房间做一次retain操作
_room = [room retain];
}
}
// dealloc方法的内存管理 例
- (void)dealloc
{
[_room release];
[super dealloc];
}
@property参数
- 控制set方法的内存管理
- retain:release旧值,retain新值(用于OC对象)
- assign:直接赋值,不做任何内存管理(默认,用于非OC对象类型)
- copy:release旧值,copy新值(一般用于NSString *)
- 控制需不需要生成set方法
- readwrite:同时生成set方法和get方法(默认)
- readonly:只会生成get方法
- 多线程管理
- atomic:性能低(默认)
- nonatomic:性能高
- 控制set方法和get方法的名称
- setter:设置set方法的名称,一定有个冒号:
- getter:设置get方法的名称
ARC下@property参数
- strong:用于OC对象, 相当于MRC中的retain
- weak:用于OC对象, 相当于MRC中的assign
- assign:用于基本数据类型, 跟MRC中的assign一样
autorelease使用
- autorelease是一种支持引用计数的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
- 在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)
- 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
@autoreleasepool
{ // 创建一个自动释放池
Person *p = [[Person new] autorelease];
} // 销毁自动释放池(会给池子中所有对象发送一条release消息)
自动引用计数ARC
ARC的判断原则
- 只要还有一个强指针变量指向对象,对象就会保持在内存中
强指针
- 被
__strong
修饰的指针,默认所有指针变量都是强指针
弱指针
- 被
__weak
修饰的指针
Category 和 Extension
category分类 \ 类别 \ 类目 (一般叫分类)
- 作用:可以在不修改原来类的基础上, 为这个类扩充一些方法
- 注意:分类只能增加方法, 不能增加成员变量
- 注意:分类中写property只会生成方法声明
- 注意:分类可以访问原来类中的成员变量
- 注意:调用顺序为 分类——原来类——父类
类扩展(Class Extension) 是Category的一个特例
- 可以为某个类扩充一些私有的成员变量和方法。写在.m文件中
// 类扩展书写格式
// 对比分类, 就少了一个分类名称,因此也有人称它为”匿名分类”
@interface 类名 ()
@end
Block
Block是iOS中一种比较特殊的数据类型
// Block的定义格式
返回值类型 (^block变量名)(形参列表) = ^(形参列表) {
};
// 调用Block
block变量名(实参);
Block注意事项
- 在
block
内部可以访问block
外部的变量 -
block
内部也可以定义和block
外部的同名的变量(局部变量),此时局部变量会暂时屏蔽外部 - 默认情况下,
block
内部不能修改外面的局部变量,是值传递 -
block
内部可以修改使用__block修饰的局部变量
Block是存储在堆中还是栈中
- 默认
block
存储在栈中,如果对block
进行一次copy
操作,block
会转移到堆中 -
block
在栈中,block
中访问外部对象,不会对对象进行retain
操作 -
block
在堆中,block
中访问外部对象,会对外界对象进行一次retain
Protocol
- Protocol的定义和遵守协议
// Protocol的定义
@protocol 协议名称
@required // 如果遵守协议的类不实现会报警告
@optional // 如果遵守协议的类不实现不会报警告
@end
// 类遵守协议
@interface 类名 : 父类 <协议名称1, 协议名称2,…>
@end
- Protocol和继承区别
继承之后默认就有实现, 而protocol
只有声明没有实现
相同类型的类可以使用继承, 但是不同类型的类只能使用protocol
protocol
可以用于存储方法的声明, 可以将多个类中共同的方法抽取出来, 以后让这些类遵守协议即可
Foundation框架
Foundation框架提供了非常多好用的类, 比如
/**
1、NSString 字符串
NSString 比较
- (BOOL)isEqualToString:(NSString *)aString 比较的是内容
- stingA == stringB 比较的是地址
- NSURL URL的全称是Uniform Resource Locator(统一资源定位符)
*/
NSString *str = @"ltd"; // 常量区中的字符串只要内容一致, 不会重复创建
/**
2、NSMutableString 可变字符串
*/
NSMutableString *strM = [NSMutableString string];
/**
3、NSArray 数组
*/
NSArray *arr = @[@"Jack", @"Rose", @"Jim"];
/**
4、NSMutableArray 可变数组
*/
NSMutableArray *arrM = [NSMutableArray array];
/**
5、NSDictionary 字典
*/
NSDictionary *dict = @{@"name":@"lnj", @"phone":@"18682057731", @"address":@"天朝"};
/**
6、NSMutableDictionary 可变字典
*/
/**
Q:NSDictionary 和 NSArray 对比
- NSArray是有序的,NSDictionary是无序的
- NSArray是通过下标访问元素,NSDictionary是通过key访问元素
*/
/**
7、NSNumber
NSNumber可以将基本数据类型包装成对象,这样就可以间接将基本数据类型存进NSArray\NSDictionary中
*/
NSNumber *num = @10;
/**
8、NSValue
NSNumber是NSValue的子类, 但NSNumber只能包装数字类型
NSValue可以包装任意值, 可以用NSValue将结构体包装后,加入NSArray\NSDictionary中
+ (NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;
+ value参数 : 所包装数据的地址
+ type参数 : 用来描述这个数据类型的字符串, 用@encode指令来生成
*/
/**
9、NSDate 时间
*/
// ————————————基本使用————————————
NSDate *now = [NSDate date];
// 设置转换后的目标日期时区
NSTimeZone *zone = [NSTimeZone systemTimeZone];
// 得到源日期与世界标准时间的偏移量
NSInteger interval = [zone secondsFromGMTForDate: date];
// 在当前时间基础上追加时区差值
now = [now dateByAddingTimeInterval:interval];
// ————————————格式化日期————————————
// 创建时间
NSDate *now = [NSDate date];
// 创建时间格式化
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
// 指定格式
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
// 格式化时间
NSString *str = [formatter stringFromDate:now];
// ————————————日历对象————————————
// 1.确定时间
NSString *time1 = @"2015-06-23 12:18:15";
NSString *time2 = @"2015-06-28 10:10:10";
// 2.将时间转换为date
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSDate *date1 = [formatter dateFromString:time1];
NSDate *date2 = [formatter dateFromString:time2];
// 3.创建日历
NSCalendar *calendar = [NSCalendar currentCalendar];
NSCalendarUnit type = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
// 4.利用日历对象比较两个时间的差值
NSDateComponents *cmps = [calendar components:type fromDate:date1 toDate:date2 options:0];
// 5.输出结果
NSLog(@"两个时间相差%ld年%ld月%ld日%ld小时%ld分钟%ld秒", cmps.year, cmps.month, cmps.day, cmps.hour, cmps.minute, cmps.second);
/**
10、NSFileManager
*/
NSFileManager *manager = [NSFileManager defaultManager];
Foundation框架常见的结构体
-
NSPoint
和CGPoint
-
NSSize
和CGSize
-
NSRect
和CGRect
Copy
特点
- 修改源对象的属性和行为,不会影响副本对象
- 修改副本对象的属性和行为,不会影响源对象
使用
- 一个对象可以调用
copy
或mutableCopy
方法来创建一个副本对象 -
copy
: 创建的是不可变副本(如NSString、NSArray、NSDictionary) -
mutableCopy
:创建的是可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)
前提
-
copy
: 需要遵守NSCopying
协议,实现copyWithZone:
方法 -
mutableCopy
: 需要遵守NSMutableCopying
协议,实现mutableCopyWithZone:
方法
深复制和浅复制
- 深复制本质是
产生了新的对象
、浅复制本质是没有产生新的对象
- 只有源对象和副本对象都不可变时,才是
浅复制
,其它都是深复制
@property中的copy的作用
- 防止外界修改内部的值
- 防止
block
中使用的外部对象已经被释放
注意:
copy block
之后引发循环引用
如果对象中block
又用到了对象自己,为了避免内存泄漏,应将对象修饰为__block