这是我参与「第四届青训营 」笔记创作活动的第3天
总览
概念
-
xxx.h 和 xxx.m
- xxx.h包含可以访问的「属性」和可以调用的「方法」
- xxx.m用来实现这些方法
-
命名空间的处理
- 使用2-4个字母的前缀使得每个类名称独特
基础语法
-
小的点
-
@表示接下来执行的代码是OC而不是C
-
@autoreleasepool 完成时自动释放内存
-
#import <Foundation/Foundation.h>预处理指令,引入头文件,import保证了头文件只会被引入一次- 导入自己的头文件,用的是双引号
- 导入系统的头文件,用的是尖括号
-
常量指针
const NSString *first = @"Hello"; NSString const *first = @"Hello"; NSString * const first = @"Hello"; // 常量指针
-
-
NSInteger
-
NSInteger和CGFloat会根据CPU的不同自动选择浮点数
-
在绝大多数情况下面你要使用 NSInteger 和 CGFloat
-
// incorrectly try to use an NSInteger with %dNSLog(@"%ld", (long)i);
-
数据类型
字符串
在OC中字符串是一个引用类型。NSString是一个Unicode(UTF-16)类型
-
创建
NSString *foo = @"Hello, world!";NSString *output = [NSString stringWithFormat:@"You picked %ld", (long)number];NSString *output = [[NSString alloc] initWithFormat:@"You picked %ld", (long)number];
-
常用方法
- stringByReplacingOccurrencesofString: 替换字符串内容
- stringByAppendingString: 合并字符串
- stringByAppendingFormat: 合并带有合适的字符串
- substringFromIndex: 截取字符串
- componentsSeparatedByString: 拆分字符串,以数组的形式呈现
- integerValue/doubleValue/floatValue: 把字符串转换成整数或浮点数
- boolValue: 把字符串转换成布尔值,如果字符串以 "Y, y, T, t" 或数字 1~9 开头,则为 true。
-
可变字符串
-
NSString是不可变的,NSMutable是可变的
-
推荐把可变字符串放到不可变的容器里
-
不推荐把不可变的字符串放到可变的容器里
-
创建可变字符串
- 第一种是创建一个现有的字符串的备份
NSString *hello = [@"Hello" mutableCopy]; - 第二种是用 NSMutableString 的初始化器来创建
NSString *hello = [NSMutableString stringWithFormat:@"..."];
- 第一种是创建一个现有的字符串的备份
-
数字
NSNumber是一个对象,数据存储在这个对象里。它之所以会存在,是因为OC的数组和字典只能存储对象,不能直接放整数,也不能放浮点数和bool
所以要用NSNumber包一下
NSNumber *ten = [NSNumber numberWithInteger:10];
NSNumber *integerTen = @10;
数组
-
创建
-
NSArray *villains = @[@"Weeping Angels", @"Cybermen", @"Daleks", @"Vashta Nerada"]; -
NSArray *villains = [NSArray arrayWithObjects:@"Weeping Angels", @"Cybermen", @"Daleks", @"Vashta Nerada", nil];- 结尾需要有
nil,告知NSArray数组的结尾
- 结尾需要有
-
-
常用方法
-
count: 返回数组中元素的数量
-
indexOfObject: 返回元素所在位置
- 如果访问不存在的对象,会返回一个很大的数字
-
objectAtIndex: 返回某一位置的元素
-
componentsJoinedByString: 把数组转换成字符串
-
-
排序
-
NSArray *sorted = [villains sortUsingSelector:@selector(compare:)];- 这里的compare不能忘记:冒号
-
-
高级用法
- makeObjectsPerformSelector
- enumerateObjectsUsingBlock
- filteredArrayUsingPredicate
字典
在Swift中字典是有序的,而Object-C里面是无序的
-
创建
@{…}- dictionaryWithObjectsAndKeys 先写值再写键
NSDictionary *ships = [NSDictionary dictionaryWithObjectsAndKeys: @"Serenity", @"Firefly", @"Enterprise", @"Star Trek", @"Executor", @"Star Wars", nil ]; // 其实是这样的。。。好反人类 { Firefly = Serenity; "Star Trek" = Enterprise; "Star Wars" = Executor; }- 两种方法不要混着用
-
其他方法
- count
- allKeys
- allValues
集合
- NSSet
- NSMutableSet
- NSCountedSet
OC中的泛型
先说结论:没有Swift中的好用,大多数OC中也不会用到。底层是「类型擦除」
NSValue 、NSData
整数可以用NSNumber包一下,CGRect、CGSize、CGPoint都是结构体而非对象,需要用NSValue包一下
NSData和Swift里一模一样
NSObject
每一个类都会继承自NSObject,一个包含了类和协议的实现文件,包括这些常用方法
- copy
- mutableCopy
- isKindOfClass :参数为 [SomeClass class],判断某一对象是否是某一类型或这一类型的子类。
- conformsToProtocol :如果一个对象遵从了某一协议,则返回 true
- respondsToSelector :检查对象上能否运行某一方法
- performSelector :在一个对象上运行某一方法
id 和 instancetype
id是一个数据类型,表示「任意OC对象」。它是一个指针,可以指向任意对象。
用instancetype去代替,返回当前类对应的实例
Block
就是Swift中的闭包。
// Swift 中
let universalGreeting = {
print("Bah-weep-graaaaagnah wheep nini bong")
}
universalGreeting()
// OC 中
// ^printUniversalGreeting : 把block放到一个叫printUniversalGreeting的变量
// (void) 不需要参数
// ^{...}
void (^printUniversalGreeting)(void) = ^{
NSLog(@"Bah-weep-graaaaagnah wheep nini bong");
};
printUniversalGreeting();
// 有参数的情况
// NSString* 返回一个字符串
// (NSString *) 必须要有NSString类型的参数
// =^ 把区块的返回值传给这个变量
// (NSString *name) 可选字符串参数
NSString* (^printUniversalGreeting)(NSString *) = ^(NSString *name) {
return [NSString stringWithFormat:@"Live long and prosper, %@.",name];
};
还包括
-
在blocks中获取值
- NSInteger __block number = 0;
- __block NSInteger number = 0;
-
循环引用
- swift里用weak 和 unowned解决,oc里用__weak
项目一
这个项目主要考虑了字符串的处理,需要OC与C一起使用
-
unichar
- 在NSString中使用,表示字符串中的每一个字符
- 不是一个对象,不能放到数组里面
-
c语言的字符数组转为OC的NSString
char cstring[256]; NSString *input = [NSString stringWithCString:cstring encoding:NSUTF8StringEncoding]; -
OC中得到字符转为NSString
unichar letter = [input characterAtIndex:0]; NSString * letterString = [NSString stringWithFormat:@"%C",letter];
类
// person.h
@interface Person : NSObject
- (void)printGreeting;
@end
// person.m
#import "Person.h"
@implementation Person
// 没有括号
- (void)printGreeting {
NSLog(@"Hello");
}
@end
提到了performSelector 的一个坑,选择器不会在意方法在哪里被声明,只要声明了就可以了。
方法
-
- (void)printGreetingTo:(NSString*)name atTimeOfDay:(NSString*)time;-
atTimeOfDay是参数time的别名,可以不给第一个参数起名字,但后面的每一个参数都需要取一个名字 -
如果用preformSelector
[person performSelector:**@selector**(printGreeting:atTimeOfDay:) withObject:@"hello" withObject:@"wo"]
-
-
多返回值可以用返回字典实现
-
-是实例方法+是类方法-
如何调用类方法
+ (instancetype)personWithName:(NSString*)name { return [[self alloc] initWithName:name]; }
-
属性
-
实例变量
- 在interface最后加上括号
- @public 表示的是全局变量,不加的话意味着只能在类的内部被访问到
- 访问实例变量用→
@interface Person : NSObject { @public NSString *name; } - (void)printGreeting; @end -
property 属性(属性和实例变量的区分)
-
用property后创建实例变量就不用花括号了
-
默认增加了get和set方法
-
用@property声明name,xcode自动创建name和setName,还创建了_name
-
_name是自动生成的,也可以改名字
@synthesize name = userName;要再.m文件中
-
-
可以用.符号去拿到属性值
@interface Person : NSObject @property NSString *name; - (void)printGreeting; @end Person *person = [Person new]; person.name = @"Taylor"; // 方法一 [person setName:@"Taylor"]; // 等价方法一 - (void)printGreeting { NSLog(@"Hello, %@!", self.name); // 方法二 NSLog(@"Hello, %@!", [self name]); // 方法三 NSLog(@"Hello, %@!", _name); // 方法四 } -
除了在自定义get和set里面要直接用实例变量,其他地方尽量都用属性
-
-
私有属性
- 使用类扩展,实现一个类的第二个接口,第二个接口写在xxx.m文件里
- 只能写自己类的类扩展,不能写系统类的扩展
@interface Person() @property NSString *name; @end @implementation Person - (void)printGreeting{ } @end -
属性修饰符
-
种类
- strong: 属性的默认特征,也就是强引用,表示「储存在内存里」。
- weak: 弱引用,用于避免循环引用。
- copy: 拷贝,当属性有对象的时候,自动复制。
- assign: 用来修饰基本数据类型,确保把值给了实例变量。
- nonatomic: 原子性(atomic)属性,是指在多线程运行的时候,某一线程访问存或者取方法,其他线程不可以进入该存、取方法。非原子性(non-atomic)则相反,你要保证没有同时存取。
- retain: strong 的旧格式,如果你看到了这个修饰符,说明真的接手了一个超级老的项目。
- readonly: 只读,不能使用 setter 访问器。
- readwrite: 读写,所有属性的默认特征,可以使用 getter 和 setter 访问器。
- atomic: 看上面的 nonatomic,atomic 让代码更安全,也是所有属性的默认特征,会降低性能。
- getter=: 改变 getter 访问器的名称。
- setter=: 改变 setter 访问器的名称。
-
默认修饰符
@property (strong, atomic, readwrite) NSString *name;
-
-
属性调整
在OC中无法同时完成对属性的读写,要分开来写
CGRect frame = button.frame; button.frame = CGRectOffset(frame, 0, 50);
创建对象
-
特点
- 在 Objective-C 里面你不需要给属性设定默认值, Objective-C 会自动把对象设定为 nil,数字设定为 0。
- 在你初始化自己的属性之前,你必须要调用父类的 init 方法。
- 在你初始化完你的你自己的属性之前,你可以在你的初始化器里调用其他的方法。
- 所有的初始化器都默认是失败的,「失败」指的是它们会返回 nil,在继续之前下一次的操作之前,你必须要先去确认初始化是否成功。
- 你需要用 self 来返回。
// 为什么要这么初始化
- (instancetype)initWithName:(NSString*)name {
if (self = [super init]) {
self.name = name;
}
return self;
}
Categories 种类
种类是类扩展的名称的集合
协议
空特性
-
Xcode的Assistant → Counterparts 可以将oc转为swift
-
nullable和nonnull
-
NS_ASSUME_NONNULL_BEGIN 和 NS_ASSUME_NONNULL_END 来保证元素非空
- 这样就不用导出写nonnull了
- 可以额外设置
- (nullable instancetype)initWithName:(NSString*)name;
-
null_resettable用于设置默认值