concept
- Foundation & Core Foundation
framework | 说明 |
---|
Foundation | OC 对象 |
Core Foundation | C 结构体类型 |
__bridge
& __bridge_retained
& __bridge_transfer
桥接方法 | 说明 |
---|
__bridge | |
__bridge_retained | Foundation -> Core Foundation |
__bridge_transfer | Core Foundation -> Foundation |
类型 | 相同点 | 不同点 |
---|
id | 可以表示任何对象 | 可以用在返回值和参数类型上 |
instancetype | 可以表示任何对象 | 只能用在返回值类型上,编译器会检测 instancetype 的真实类型 |
方法 | 调用时机 | 子类实现 | 分类实现 | 调用顺序 |
---|
load | 第一次装载进内存时调用 | 不会覆盖父类 | 不会覆盖原始类 | 父类 -> 子类 -> 分类 |
initialize | 第一次使用这个类时调用 | 会覆盖父类 | 会覆盖原始类 | 父类 or 子类 or 分类 |
属性 | 特性 | 说明 |
---|
readwrite | 可读可写 | 生成 getter 方法和 setter 方法 |
readonly | 只读 | 只会生成 getter 方法,不会生成 setter 方法 |
retain | 持有 | setter 方法将传入参数先持有(retain count +1),再赋值 |
nonatomic | 非原子 | 非线程安全,编译更快 |
atomic | 原子 | 原子操作,保证线程安全 |
assign | 赋值 | setter 方法将传入参数赋值给实例变量 |
weak | 弱引用 | 对象被销毁时,会被设置nil |
strong | 强引用 | |
copy | 复制 | 类似 strong,在赋值时进行 copy 而不是 retain,用于不可变对象,防止使用过程中被篡改(如:字符串,block) |
unsafe_unretained | 不安全的不持有 | 类似 weak,对象销毁时,不会被设置为 nil,比 weak 效率更高 |
nonnull | 不为空 | |
nullable | 可为空 | |
修饰符 | 说明 |
---|
@synthesize | 编译器自动生成 getter 和 setter |
@dynamic | 显式表示不希望编译器生成 getter 和 setter |
类型 | 加载时机 | 说明 |
---|
category | 运行时 | 分类,用于扩展现有类功能 |
extension | 编译时 | 一种特殊的(匿名) category,只能存在原始类 .m 文件中 |
#include
& #import
& @class
类型 | 说明 |
---|
#include | C/C++ 导入关键字 |
#import | OC 导入关键字,只导入一次,不会重复导入 |
@class | 头文件引用类 |
iOS 9,泛型使用
类型 | 说明 |
---|
__covariant | 协变,子类转父类 |
__contravariant | 逆变,父类转子类 |
KVC & KVO
KVC
(Key Value Coding) 是一种间接更改对象状态的方式,使用字符串来描述对象需要更改的属性(以字符串的形式向对象发送消息)。
valueForKey:
setValue:ForKey:
valueForKeyPath:
setValue:forKeyPath:
dictionaryWithValuesForKeys:
dictionaryWithObjects:forKeys:
setValuesForKeysWithDictionary:
NSNumber *count = [student.books valueForKeyPath:@"@count"];
NSNumber *count = [student valueForKeyPath:@"books.@count"];
NSNumber *sum = [student.books valueForKeyPath:@"@sum.price"];
NSNumber *sum = [student valueForKeyPath:@"books.@sum.price"];
NSArray *prices = [student.books valueForKeyPath:@"@distinctUnionOfObjects.price"];
NSArray *prices = [student valueForKeyPath:@"books.@distinctUnionOfObjects.price"];
KVO
(Key Value Observing) KVO 是一种非常重要的机制,它允许监听对象的属性的变化。
-(void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
-(void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
- (void)setAge:(NSString *)age {
[self willChangeValueForKey:@"age"];
_age = age;
[self didChangeValueForKey:@"age"];
}
- (NSString *)age {
return _age;
}
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqualToString:@"age"]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey:key];
}
Block
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
@property (nonatomic, copy, nullability) returnType (^blockName)(parameterTypes);
- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;
[someObject someMethodThatTakesABlock:^returnType (parameters) {...}];
void SomeFunctionThatTakesABlock(returnType (^blockName)(parameterTypes));
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};
类型 | 说明 |
---|
NSMallocBlock | 存储于堆区 |
NSStackBlock | 存储于栈区 |
NSGlobalBlock | 存储于程序数据区 |
typedef void (^blk)(void);
blk returnBlock() {
int val = 11;
return ^{ NSLog(@"val = %d", val); };
}
void testBlock() {
__block int val = 10;
__strong blk strongPointerBlock = ^{ NSLog(@"val = %d", ++val); };
strongPointerBlock();
NSLog(@"strongPointerBlock: %@", strongPointerBlock);
__weak blk weakPointerBlock = ^{ NSLog(@"val = %d", ++val); };
weakPointerBlock();
NSLog(@"weakPointerBlock: %@", weakPointerBlock);
blk copyBlock = [weakPointerBlock copy];
copyBlock();
NSLog(@"copyBlock: %@", copyBlock);
NSLog(@"tempBlock %@", ^{ NSLog(@"val = %d", ++val); } );
NSLog(@"returnBlock: %@", returnBlock());
}
类型 | 说明 |
---|
Block | 一个函数对象,包含函数信息,有类型限制 |
函数指针 | 是一个地址,不具备函数原型信息,没有类型限制 |
int (*func)(int, int);
int (^block)(int, int);
(*func)(10, 20);
block(10, 20);
- 解除循环引用
- 局部变量默认都是强引用的,离开其所在的作用域之后就会被释放
- 使用
__weak
关键字,可以将局部变量声明为弱引用 - 在 Block 中引用
weakSelf
,则 Block 不会再对 self
做强引用
@property (nonatomic, strong) NSMutableArray *myBlocks;
int(^sum)(int, int) = ^(int x, int y) {
return [self sum:x y:y];
};
[self.myBlocks addObject:sum];
__weak DemoObj *weakSelf = self;
int(^sum)(int, int) = ^(int x, int y) {
return [weakSelf sum: x y: y];
};
Copy
- 深复制 & 潜复制
只有不可变对象创建不可变副本(copy)才是浅复制,其他都是深复制
复制类型 | 说明 |
---|
深复制 | 内容拷贝,源对象和副本指向的是不同的两个对象;源对象引用计数器不变,副本计数器设置为1 |
浅复制 | 指针拷贝,源对象和副本指向的是同一个对象;对象的引用计数器+1 |
Copy
& MutableCopy
使用 copy
或 mutableCopy
方法可以创建一个对象的副本
复制类型 | 说明 |
---|
copy | 需要实现NSCopying 协议,创建的是不可变副本(如NSString 、NSArray 、NSDictionary ) |
mutableCopy | 需要先实现NSMutableCopying 协议,创建的是可变副本(如NSMutableString 、NSMutableArray 、NSMutableDictionary ) |
源对象 | 复制方式 | 副本 |
---|
NSArray NSMutableArray | copy | NSArray |
| mutableCopy | NSMutableArray |
NSDictionary NSMutableDictionary | copy | NSDictionary |
| mutableCopy | NSMutableDictionary |
NSString NSMutableString | copy | NSString |
| mutableCopy | NSMutableString |
- 自定义类添加复制功能
如果想自定义 copy
, 那么就必须遵守 NSCopying
, 并且实现 copyWithZone:
方法
如果想自定义 mutableCopy
, 那么就必须遵守 NSMutableCopying
, 并且实现 mutableCopyWithZone:
方法
- (id)copyWithZone:(NSZone *)zone {
id copy = [[[self class] allocWithZone: zone] init];
return copy;
}
- (id)copyWithZone:(NSZone *)zone {
Person *p = [[[self class] allocWithZone: zone] init];
p.name = self.name;
p.age = self.age;
return p;
}
Foundation
NSObject
- (BOOL)isKindOfClass:(Class)aClass
- (BOOL)isMemberOfClass:(Class)aClass
- (BOOL)conformsToProtocol:(Protocol)aProtocol
+ (BOOL)instancesRespondToSelector:(SEL)aSelector
- (BOOL)respondsToSelector:(SEL)aSelector
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
nil & NIL & NSNull
类型 | 说明 |
---|
nil | 一个空地址,可以存放在集合中 |
NIL | 等价于 nil |
NSNull | 一个指向 nil 的对象,不能存放在集合中 |
+ (NSNull *)null;
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:[NSNull null] forKey:@"work number"];
id value = [dict valueForKey:@"work number"];
if (value == [NSNull null]) {
NSLog(@"work number doesn't exist.");
}
NSNumber & NSValue
类型 | 说明 |
---|
NSNumber | 将基本数据类型包装成对象 |
NSValue | 可以包装任意值(包括结构体) |
NSString & NSMutableString
类型 | 说明 |
---|
NSString | 不可变的,不能删除字符或者添加字符。 |
NSMutableString | NSString 的子类,可变字符串 |
NSArray & NSMutableArray
类型 | 说明 |
---|
NSArray | 用来存储对象的有序列表,它是不可变的 |
NSMutableArray | NSArray 的子类,可变有序列表,可以添加和删除 |
- (void)makeObjectsPerformSelector:(SEL)aSelector;
- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)argument;
NSDictionary & NSMutableDictionary
注 : key 需要实现 NSCopying
协议
类型 | 说明 |
---|
NSDictionary | 不可变的键值对存储对象 |
NSMutableDictionary | NSDictionary 的子类,可变对象 |
NSAttributedString
属性名 | 说明 |
---|
NSKernAttributeName | 调整字句 (kerning 字句调整) |
NSFontAttributeName | 设置字体 |
NSForegroundColorAttributeName | 设置文字颜色 |
NSParagraphStyleAttributeName | 设置段落样式 |
NSBackgroundColorAttributeName | 设置背景颜色 |
NSStrokeColorAttributeName | 设置文字描边颜色,需要和NSStrokeWidthAttributeName 设置描边宽度,这样就能使文字空心. |
NSStrokeWidthAttributeName | 设置描边宽度 |
NSStrikethroughStyleAttributeName | 添加删除线,(strikethrough 删除线) |
NSUnderlineStyleAttributeName | 添加下划线 |
NSShadowAttributeName | 设置阴影,单独设置不好使,必须和其他属性搭配才好使。 |
NSVerticalGlyphFormAttributeName | 该属性所对应的值是一个 NSNumber 对象(整数)。0 表示横排文本。1 表示竖排文本。在 iOS 中,总是使用横排文本,0 以外的值都未定义。 |
NSObliquenessAttributeName | 设置字体倾斜。(Skew 斜) |
NSExpansionAttributeName | 设置文本扁平化 |
CGPoint & CGSize & CGRect
> 定义位置 :`CoreGraphics/CGGeometry.h`
结构体 | 创建方法 | 说明 |
---|
CGPoint | CGPointMake() | 平面中的一个点 (x, y) 的位置 |
CGSize | CGSizeMake() | 宽度和高度 |
CGRect | CGRectMake() | 位置和宽高 |
NSRange
这个结构体用来表示一个范围
NSRange range;
range.location = 7;
range.length = 3;
NSRange range = {7, 3};
NSRange range = {.location = 7, .length = 3};
NSRange range = NSMakeRange(7, 3);
NSDate
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSDate *date = [formatter dateFromString:@"2018-10-29 00:00:00"];
NSLog(@"%@", [formatter stringFromDate: date]);
NSAssert
断言NSAssert
仅在 debug 版本起作用,用于检查“不应该”发生的情况。可以把NSAssert
看成一个在任何系统状态下都可以安全使用的无害测试手段
NSPredicate
谓词,用于判断条件表达式的求值返回真或假的过程
特点
- 谓词中的匹配指令使用大写字母
- 谓词中可以使用格式字符串
- 如果通过对象的 key path 指定匹配条件,需要使用 %K
逻辑指令
&&
||
!
<
<=
==
>
>=
BETWEEN {}
字符串匹配
字符串匹配 | 说明 |
---|
BEGANWITH | 以指定字符开始 |
ENDSWITH | 以指定字符结束 |
CONTAINS | 包含指定字符,可使用修饰符,c 不区分大小写, d 不区分注音符号 |
LIKE | 使用通配符匹配, ? 一个字符,* 0个或多个字符 |
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self CONTAINS '1'"];
NSString *text = @"1234";
NSLog(@"%d", [predicate evaluateWithObject: text]);
NSMutableArray *result = [NSMutableArray arrayWithCapacity: personList.count];
for (NSInteger i = 0; i < personList.count; i++) {
Person *person = personList[i];
if (person.age > 5 && person.age < 15 && NSNotFound != [person.name rangeOfString:@"1"].location) {
[result addObject: person];
}
}
return result;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name CONTAINS '1' && %K BETWEEN {%d, %d}", @"age", 5, 15];
NSArray *result = [personList filteredArrayUsingPredicate: predicate];
NSTimer & CADisplayLink
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)YesOrNo;
- (void)invalidate;
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(setNeedsDisplay) userInfo:nil repeats:YES];
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)];
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
Memory Management
Reference
Basic
ARC特点
- 不允许调用
release
、retain
、retainCount
- 允许重写
dealloc
,但是不允许调用[super dealloc]
@property
的参数 strong
: 成员变量是强指针(适用于OC对象类型)weak
: 成员变量是弱指针(适用于OC对象类型)assign
: 适用于非OC对象类型
- 以前的
retain
改为用strong
Compiler Flags
编译标识 | 说明 |
---|
-fobjc-arc | MRC 项目中,对使用 ARC 文件的标识 |
-fno-objc-arc | ARC 项目中,对使用 MRC 文件的标识 |
ARC & MRC & @autoreleasepool
ARC 中是使用散列表实现的,64位系统中,包含64张 SideTables
,包含 自旋锁 & 引用计数表 &弱引用表
,引用计数表中的 分离锁
可以将一张表拆成多部分,实现并发操作。
ARC 编译阶段 : 在合适的地方插入 retain
和 release
ARC 运行时阶段 : 处理 weak
修饰变量,引用计数为0时,将其设为 nil
@autoreleasepool 实现
AutoreleasePoolPage
使用双向链表实现,当链表容量满了,会在链表的顶端指向下一张表。
悬垂指针 & 野指针
指针 | 说明 |
---|
悬垂指针 | 指针内存已经释放,但指针还存在,会出现 BAD_ACCESS 错误。 |
野指针 | 没有进行初始化的指针 |
Usage
@property (nonatomic, retain) NSString *name;
- (void)setName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name retain];
}
}
@property(nonatomic, copy) NSString *name;
- (void)setName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name copy];
}
}
Instrument 内存检测原理
工具 | 说明 |
---|
Leaks | 检测是否未写 release (ARC 已经不常见),C 代码是否未写 free |
Allocations | 主要思路是在一个时间切片内检测对象的声明周期以观察内存是否会无限增长。通过 hook 掉 alloc,dealloc,retain,release 等方法,来记录对象的生命周期。 |
Multithread
iOS 的三种多线程技术
多线程技术 | 说明 |
---|
NSTread | 创建线程简单,管理线程困难 |
GCD | 基于 C 语言的 API,使用 block 定义任务,使用方便 |
NSOperation | 基于 GCD 封装的面向对象 API,提供更多线程管理特性(如:限制最大并发数量、操作之间的依赖关系) |
NSTread
NSThread
的多线程不会自动使用 @autoreleasepool
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
[NSThread currentThread]
[NSThread sleepForTimeInterval:2.0f];
GCD
GCD 的函数都是以 dispatch (分派、调度)开头的
dispatch_queue_t
: 队列dispatch_sync
: 同步操作dispatch_async
: 异步操作
dispatch_barrier_async(queue, ^{
NSLog(@"barrier");
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
});
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"onece");
});
dispatch_apply(subpaths.count, queue, ^(size_t index) {
});
dispatch_group_t group = dispatch_group_create();
dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
dispatch_group_enter(group);
dispatch_group_leave(group);
dispatch_semaphore();
NSOperation
self.myQueue = [[NSOperationQueue alloc] init];
- (void)operationAction:(id)obj {
NSLog(@"%@ - obj : %@", [NSThread currentThread], obj);
}
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget: self selector:@selector(operationAction:) object:@"Invocation Operation"];
[self.myQueue addOperation:op];
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[self operationAction:@"Block Operation"];
}];
[self.myQueue addOperation:op];
Lock
多线程锁,保证线程安全,确保同一时刻只有一个线程访问共享资源。
多线程锁 | 说明 |
---|
NSLock | |
NSCondition | |
NSConditionLock | 条件锁,满足条件才加锁 |
NSRecursiveLock | 递归锁,在一个线程中可以反复加锁 |
NSDistributedLock | 分布式锁,同时操作同一张表中的不同部分 |
dispatch_semaphore | GCD 信号量实现加锁 |
pthread_mutex | 互斥锁, |
pthread_mutex(recursive) | 递归锁, |
@synchronized(self) { ... } | 同步锁, |
OSSpinLock | 自旋锁,不停探测锁是否被释放 (不推荐,会造成优先级反转) |
pthread_rwlock | |
POSIX Conditions | |
os_unfair_lock | |
锁的分类
类型 | 说明 |
---|
互斥锁 | 进入休眠,锁被释放之后,被唤醒 |
自旋锁 | 进入轮询,锁被释放之后,立刻执行 (效率高于互斥锁,代价高) |
条件锁 | |
递归锁 | |
分布式锁 | |
经典面试题
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
NSLog(@"1");
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"2");
});
NSLog(@"3");
dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
NSLog(@"1");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
});
NSLog(@"4");
while (1) {
}
NSLog(@"5");
__block int i = 0;
while ( i < 10000 ) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
i++;
});
}
NSLog(@"i=%d", i);