返回值是BOOL类型的 方法名一般都是is开头
编译只检测语法错误
方法声明可以重复 方法的实现不可以重复
自己的.m文件要包含自己的.h文件
定义一个类分2个文件 :.h声明文件 .m实现文件
.h : 成员变量 方法的声明
.m : 方法的实现
如果想使用某一个类,只需要#import类的.h文件即可
类的设计:
1> 类名
//类名第一个字母必须大写
//不能有下划线
//多个英文字母用驼峰标识(JumpZoombie)
2> 属性
3> 行为(功能)
//完整的写一个函数:函数的声明和定义(实现)
//完整的写一个类:累的声明和实现
//1.类的声明
//声明对象的属性和行为
//: NSObject 目的是:让Car这个类具备创建对象的能力
//因为使用了NSObject
@import <Fundation\Fundation.h>
@interface Car : NSObject
{//用来声明对象属性(实力变量\成员变量,默认会初始化为0)
@public
//@public可以让外部的指针间接访问对象内部的成员变量
int wheels;
int speed;
}
//方法(行为):方法名,参数, 返回值(声明 实现)
//只要是OC对象的方法,必须以减号-开头
//OC方法中任何数据类型都必须用小括号()括住
//OC方法中的的小括号()只有一个作用:括住数据类型
- (void)run; //申明方法
@end
//2.类的实现
//用来实现@interface中声明的方法
@implementation Car
//方法的实现(说清楚方法里面有什么代码)
- (void)run
{
NSlog(@"车子跑起来了");
}
@end
int main()
{
//在OC中,想执行一些行为,就写上一个中括号[行为执行着 行为名称]
//利用类来创建对象
//执行Car这个类的行为来创建对象
//定义了一个指针变量p,p将来指向的是Car类型的对象
//[Car new]会创建一个新对象,并且会返回新对象本身(新对象的地址)
Car *p = [Car new];
Car *p2 = [Car new];
//给p所指的对象的wheels属性赋值
p->wheels = 4;
p->speed = 250;
p2->wheels = 5;
p2->speed = 300;
//给p所指向对象发送一条run消息
[p run];
[p2 run];
NSlog(@"车子有%d个轮子,时速为:%dkm/h", p->wheels, p2->speed);
return 0;
}
NSString * str1 = @"hello world";
NSString * str2 = [[NSString alloc] initWithString:@"hello world"];
NSString * str3 = [[NSString alloc] initWithUTF8String:"hello world"];
//用C的字符串创建OC的字符串对象
NSString * str4 = [[NSString alloc] initWithFormat:@"hello %d %c", 5, 'A'];
//使用格式符,拼接成一个字符串
[str2 release];
[str3 release];
[str4 release];
//申请临时字符串对象(不需要手动释放)
NSString * str5 = [NSString stringWithUTF8String:"hello world"];
NSString * str6 = [NSString stringWithFormat:@"%d %@ %c", 1, @"hello", 'R'];
NSString * str7 = [NSString stringWithString:str1];
多态(对于对象 继承而言):父类指针指向子类对象
调用方法时会动态检测对象的真实类型
子类对象的类型也是父类这个类型
1.没有继承就没有多态
2.代码的体现:父类类型的指针指向子类对象
3.如果参数中使用的是父类类型 可以传入父类,子类的对象
多态局限性:父类类型的指针变量不能用来调用子类特有的方法
(必须强转为子类类型变量后 才能直接调用子类特有的方法)
super的作用:
1.直接调用父类中的方法
2.super处在对象方法中 那么就会调用父类中的对象方法
super处在类方法中,那么就会调用父类的类方法
3.使用场合: 子类重写父类的方法时想保留父类的一些行为
重写:子类重新实现父类中的的某个方法,覆盖父类以前的做法
父类必须声明在子类的前面
子类不能拥有父类相同的成员变量
调用某个方法优先去当前类中找 如果找不到 去父类找
继承使用场合:当两个类拥有相同属性和方法的时候 就可以将相同的东西抽取到一个父类中
当A类完全拥有B类的部分属性和方法时 可以考虑让B类继承A类
组合:xxx拥有xxx
继承:xxx是xxx
NSObject 含有isa成员变量 retaincount成员变量
断点调试
isa(成员变量)指向这个类
没有成员变量的方法尽量使用类方法
#pragma mark 标记 注释
点语法本质是set\get方法的调用(编译进行)(只要修改了类中成员变量的值就可以使用点语法)
类方法不能访问成员变量(类方法没有对象 没有对象就没有成员变量)
成员变量的作用域:
@public
任意地方都能直接(通过变量名)访问成员变量
@private
只能在当前类的对象方法中访问
@protected
能在当前类和子类中的对象方法直接访问成员变量
系统默认为@protected
在实现中写成员变量则默认为是私有的 不能和声明中的成员变量重名
OC中是单继承 父类也叫子类(superclass)
@property和@synthesize
编译器特性:
@propery可以自动生成get set 的声明
例如:
@property int age;相当于
- (void)setAge:(int)age;
-(void)age;
@property NSString *name;相当于
- (void)setName:(NSString *)name;
- (NSString)name;
@synthesize 可以自动生成get set 的实现
@synthesize age = _age;相当于
- (void)setAge:(int)age
{
_age = age;
}
- (int)age
{
return _age;
}
get方法set方法:
方法声明:
@property int speed;
@property int wheels;
方法的实现:
@synthesize speed = _speed;
@synthesize wheels = _wheels;
注:
如果在类的声明中只写:@property int age;
编译器会做三件事:1.自动生成成员变量_age(@private类型)
2.自动生成_age的set get方法的声明
3.自动生成_age的set get方法的实现
// assign直接赋值 (默认是assign)
// readonly 只生成get方法 不生成set方法
// readwrite 同时生成set get方法(默认是readwrite)
// atomic 原子性访问(默认)考虑线程安全
// nonatomic 非原子性访问
// 属性修饰符setter=setNo:, getter=no 修改方法名 调用可以使用两套 指向同一个成员变量_number
@property (assign, readwrite, atomic, setter=setNo:, getter=no) long number;
id是一种类型(万能指针包括* 可以指向\操作任何OC对象)
id ==== NSObject *
构造方法(用来初始化对象的方法):
NSObject里的构造方法
- (id)init
{
isa = [ self class];
return self;
}
完整的创建一个可用的对象[Person new]有两步
1.分配存储空间 + alloc
2.初始化 - init
//[super init] 初始化父类的成员变量
- (id) init // 重写Init方法
{
if (self = [super init])
{
_age =10;
}
return self;
}
重写构造方法的目的:为了让对象创建出来成员变量就会有一些固定的值
注意点:
1.先调用父类的构造方法([super init])
2.再进行子类内部成员变量的初始化
自定义构造方法:
- (id)initWithName:(NSString *)name;
- (id)initWithName:(NSString *)name
{
if (self = [super init])
{
_name = name;
}
return self;
}
分类 Category (只能增加方法 不能增加成员变量)(可以给某一个扩充一些方法(不修改原来类的代码))
category用于向已经存在的类添加方法以达到扩展已有类的目的。
一般类别有4种用途:
1 不继承情况下对已有类扩展
2 简化类的开发工作(多人分类别开发一个类)
3 将常用的相关方法分组
4 没有源码情况下添加(重写)方法。
//声明
@interface NSString (inverse);
@end
// 实现
@implement NSString (inverse)
@end
不同的人写不同的分类(模块)
用这个类声明的对象就可以使用不同的人写的方法
分类的方法实现中可以访问原类的成员变量
分类可以重新实现原来类的方法 但是会覆盖掉原来类的方法 原来类的方法永远失效
方法寻找的优先级:分类方法名与原类方法名重复与父类方法名重复
优先去找分类的方法 再找原类的方法 再找父类的方法
如果不同分类的方法重名 则看编译顺序 选择实现最后参与编译的文件
类的本质:
类本身也是个对象 是个class类型的对象 简称类对象
利用实例对象获取内存中的类对象
Person * p = [[Person alloc] init];
class c = [p class];
class c1 = [person class];
//c = c1 类型是Person
// Person *p1 = [c new]; // 利用这个Person类(c)创建一个Person对象
load:
//当程序启动的时候 就会加载项目中的所有类和分类 类加载完毕后就会调用每个类和分类的+load方法 只会调用一次
注:先加载父类再加载子类(分类也会加载 原始类后加载)
+ (void)load
{
NSLog(@“person-load”);
}
initialize:
//当第一次使用这个类的时候(初始化) 就会调用一次当前类的+initialize方法
+ (void)initialize
{
// 可以用来监听 类第一次使用时(初始化)会给与提示
NSLog(@“person-initialize”);
}
description:用于对象的描述
NSObject自带的方法(既有类方法又有对象方法)返回值是OC字符串
对象方法默认取对象的地址 结果:<类名 :内存地址>
类方法默认取对象的类名
还可以自己重写(最好写对象方法)
NSLog(@“%d”, _LINE_); // 打印当前函数
printf(“%s\n”, _FILE_); // 打印当前路径
NSLog(@“%s”, _func_); // 打印当前函数名
SEL(存放方法地址)(其实消息就是SEL)
每个方法里都有:SEL _cmd;
_cmd 存放该方法的地址
//_cmd == @selector(test)
Person *p = [[Person alloc] init];
[p performSelector:@selector(方法名)]; // 相当于[p test];
[p test];
// 1.把test方法包装成SEL类型的数据
// 2.根据SEL数据找到对应的方法地址
// 3.根据方法地址调用对应的方法
内存管理:(相对于堆里面的数据而言)(对于基本类型int char float double struct enum无效)
每个对象都存放4字节的引用计数器(记录对象被引用的个数 整数)
当计数器等于0对象就会被销毁(内存回收)
对象刚诞生的时候引用计数器默认为1
如果对象计数器不为0 那么在整个程序运行的过程中 它占用的内存就不可能被回收 除非整个程序已经退出
引用计数器的一些方法:
1,当给对象发送一条retain消息 可以使引用计数器+1(retain方法返回对象本身)
2,当给对象发送一条release消息 可以使引用计数器-1
3,可以给对象发送retainCount消息获得当前的引用计数器值
当一个对象被销毁时 系统会自动向对象发送一条dealloc消息
一般会重写dealloc方法 在这里释放相关资源 dealloc就像对象的遗言
注:
一旦重写了dealloc方法 就必须调用[super dealloc]
并且放在最后面调用
不能直接调用dealloc方法
概念:
1.僵尸对象:被释放(回收)的在内存中不存在的对象 僵尸对象不能再使用
2.野指针:指向僵尸对象(不可用内存)的指针 给野指针发送消息会报错
EXC_BAD_ACCESS(野指针错误)访问了一块坏的内存(已经被回收,已经不可用的指针)
3.空指针:没有指向任何东西的指针(存储的东西是nil NULL 0)
OC中不存在空指针错误 给空指针发送消息 不报错
只要还有人在用某个对象 那么这个对象就不会被回收
只要你想用这个对象 就让对象的计数器+1
当你不再使用这个对象时 就让对象的计数器-1
1.谁创建 谁release
如果你通过alloc new 或[Mutable]copy来创建一个对象 那么你必须调用release或autorelease
换句话说 不是你创建的 就不用你去[auto]release
2.谁retain 谁release
只要你调用了retain 无论这个对象是如何生成的 你都要调用release
3.有始有终 有加就有减
曾经让对象计数器加一 最后就要让对象计数器减一
总结:内存管理代码规范:
一.只要调用alloc 必须有release(autorelease) 如果对象不是alloc产生的就不需要release
二.set方法的代码规范
1>基本数据类型:直接赋值
- (void)setAge:(int)age
{
_age = age;
}
2>OC对象类型
- (void)setCar:(Car *)car
{
// 1.先判断是不是新传进来对象
if (car != _car)
{
// 2.对旧对象做一次release
[_car release];
// 3.对新对象做一次retain
_car = [car retain];
}
}
三.dealloc方法的代码规范
1>一定要[super dealloc], 而且放在最后面
2>对self(当前)所拥有的其他对象做一次release
- (void)dealloc
{
[_car release];
NSLog(@“某对象被释放!”);
[super dealloc];
}
// retain: 在生成set的方法里面release旧值,retain新值
@property (retain) NSString *name
property里面的参数
1.内存管理的参数
retain:release旧值,retain新值(适用于OC对象类型)
assign: 直接赋值(默认, 适用于非OC对象类型)
copy: release旧值,copy新值
2.是否要生成set方法
readonly:只会生成get的声明和实现
readwrite:同时生成get set的声明和实现(默认)
3.多线程管理
nonatomic: 性能高(一般就用这个)
atomic: 性能低(默认)
4.set get方法的名称
setter: 决定了set方法的名称 一定要有个冒号
getter: 决定了get方法的名称
copy 不管是可变还是不可变的copy都会生成不可变对象(不可变对象调用copy方法为浅拷贝)
mutableCopy 不管是可变还是不可变的mutableCopy都会生成可变对象(深拷贝)
@class的作用(避免循环引用):仅仅告诉编译器 某个名称是一个类
开发中引用一个类的规范
1>在.h文件中用@class来声明类
2>在.m文件中用#import来包含类的所有东西
(循环引用)解决方案:
1> 一端retain
2> 一端assign
autorelease的基本用法(对象方法 返回对象本身 延迟了对象释放的时间)
1> 会将对象放到一个自动释放池(栈)中
2> 当自动释放池被销毁时 会对池子里面的所有对象做一次release操作
3> 会返回对象本身
4>调用完autorelease方法后 对象的计数器不变
autorelease的好处
1> 不用再关心对象的释放时间
2> 不用再关心什么时候调用release
autorelease的使用注意点
1> 占用内存较大的对象不要随便使用autorelease
2> 占用内存较小的对象使用autorelease 没有太大影响
autorelease的错误写法
1>alloc之后调用了autorelease 又调用release
2>连续调用多次autorelease
自动释放池
1>在iOS程序运行过程中 会创建无数个池子 这些池子都是以栈结构存在(先进后出)
2>当一个对象调用autorelease方法时 会将这个对象放到栈顶的释放池
自动释放池的创建方式:
@autoreleasepool // 可以无限多的创建和嵌套
{ //开始代表创建了释放池
} // 结束代表销毁释放池(推荐)
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //开始代表创建了释放池
Person *p = [[[Person alloc] init] autorelease];
[pool release]; // 结束代表销毁释放池(淘汰)
1.开发过程中一般使用一个类方法快速创建一个对象 (return [[[Person alloc] init] autorelease]; // 申请一个对象并放入释放池 即返回一个autorelease的对象)
创建对象时不要直接用类名 一般用self
2.系统自带的方法里面没有包含 alloc new copy 说明返回的对象都是autorelease的
ARC 自动内存管理(编译器特性)
MRC 手动内存管理
ARC的判断的准则:只要没有强指针指向对象 就会释放对象
指针分2种:
1>强指针:默认情况下 所有的指针都是强指针 (关键字:__strong 修饰指针它就是强指针)
2>弱指针:(关键字:__weak 修饰指针它就是弱指针)
@property (nonatomic, strong) Dog *dog;
ARC特点:
1>不允许调用release retain retainCount
2>允许重写dealloc 但是不允许调用[super dealloc]
3>@property的参数
* strong: 成员变量是强指针 相当于原来的retain(适用于OC对象类型)
*weak:成员变量是弱指针 相当于原来的assign(适用于OC对象类型)
*assign:适用于非OC对象类型
4>以前的retain 改为 strong
-fno-objc-arc 把ARC环境转为MRC
-f-objc-arc 把MRC环境转为ARC
(循环引用)解决方案
1>一端strong
2>一端weak
#import <Foundation/Foundation.h>
@class Dog;
@interface Person : NSObject
@property (nonatomic, strong) Dog * dog;
@end
#import <Foundation/Foundation.h>
@class Person;
@interface Dog : NSObject
@property (nonatomic, weak) Person *person;
@end
Block(一种数据类型) 封装了一段代码 可以在任何时候执行
Block 可以作为函数参数或者函数的返回值 而其本身又可以带输入参数或返回值
苹果官方建议尽量多用block 在多线程 异步任务 集合遍历 集合排序 动画转场用的很多
定义block变量:
// 没有返回值 没有形参 :
void (^myBlock)() = ^{
NSLog(@"------");
};
myblock();
// 有返回值 有形参
int (^sumBlock)(int, int) = ^(int a, int b){
return a + b;
};
int c= sumblock(10,11);
// 利用typedef定义block类型
#import <Foundation/Foundation.h>
typedef int (*MyBlock)(int, int);
int main()
{
MyBlock sumBlock;
sumBlock = ^(int a, int b){
return a + b;
};
MyBlock minusBlock = ^(int a, int b){
return a - b;
};
NSLog(@"%d %d", sumBlock(10,9), minusBlock(10,9));
return 0;
}
block访问外面局部变量
* block内部可以访问外面的变量
* 默认情况下 block内部不能修改外面的局部变量
* 给局部变量加上__block关键字 这个局部变量就可以在block内部修改
@protocol (协议)
它可以让你轻松的定制一个中心对象周围其他几个对象的行为。监听某个对象的变化
基本用途:
可以用来声明一大堆方法(不能声明成员变量)
只要某个类遵守了这个协议 就相当于拥有这个协议中的所有方法的声明
只要父类遵守了某个协议 就相当于子类也遵守了
一个协议遵守了另外一个协议 就可以拥有另一个协议的所有方法声明
格式:
1>协议的编写
@protocol 协议名称 <NSObject>
@property (nonatomic, copy) NSString * name; // 声明一个属性
+ (…)… // 声明一个类方法
@required 要求实现(默认) 不实现就会发出警告
// 方法声明列表
@optional 不要求实现 怎样都不会警告
// 方法声明列表
@end
2>某个类遵守协议
@interface 类名 :父类名 <协议名称1,协议名称2>
@end
3>协议遵守协议
@protocol 协议名称 <其他协议名称1, 其它协议名称2>
@end
4>定义一个变量的时候 限制这个变量保存的对象遵守某个协议
类名<协议名称> *变量名;
id<协议名称> 变量名
例:
NSObject<myProtocol> *obj;
id<myProtocol> obj2;
如果没有遵守对应的协议 编译器会警告(严重警告)
5>@property中声明的属性也可以用作一个遵守一个协议的限制
@property (nonatomic, strong) 类名<协议名称> *属性名;
6>协议可以定义在单独的.h文件中 也可以定义在某个类中
1. 如果这个协议只用在某个类中 应该把协议定义在该类中
2.如果这个协议用在很多类中 就应该定义在单独文件中
7>分类可以定义在单独.h文件中 也可以定义在原来类中
1. 一般情况下 都是定义在单独文件
2. 定义在原来类中的分类 只要求能看懂语法
Foundation框架:
集合:Array set dictionary
NSRange(location , length)
NSMakeRange(location length)
找不到 length = NSNotFound = -1
NSPoint\CGPoint
CGpoint p1 = NSMakePoint(10,10);
NSPoint p2 = CGPointMake(20,20); // 常用
CGPointMake(0,0) == CGPonitZero // 表示原点
NSSize\CGSize
NSSize s1 = CGSizeMake(100,50); // 常用
CGSize s2 = NSMakeSize(200,60);
CGSizeMake(0,0) == CGSizeZero
NSRect\CGRect
CGRect r1 = CGRectMake(0, 0, 100, 50);
CGRect r2 = {{0, 0},{100,90}};
CGRect r3 = {p1, s1};
CGRectMake({0,0},{0,0}) == CGRectZero
NSString\NSMutableString
NSUTF8StringEncoding 用到中文就可以用这种编码
URL:资源路径
网络资源:协议头://路径
本地资源:file://绝对路径(以/开头的才叫绝对路径)
NSURL *url = [[NSURL alloc] initWithString:@“ 资源 ”];
NSError * error = nil;
NSString * dicString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
if (error) {
NSLog(@"文件读取错误!");
} // 读取path路径里的内容转化为OC字符串
NSArray\NSMutableArray (有序)
遍历:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *array = @[@"1", @"2", @"3", @"4"];
// 用Block代码块遍历数组
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"%ld-%@", idx, obj);
// 停止遍历
if (idx == 2) {
*stop = YES;
}
}];
}
return 0;
}
NSDictionary\NSMutableDictionary (无序)
id obj = dict[@“key”]; // 取出对应键的值
只允许有一个key 可以有多个value
NSSet (无序)
NSSet *set = [NSSet setWithObject:….,nil];
id obj = [set anyObject]; // 随机拿出一个元素
NSNumber
@20 将20包装成NSNumber对象(编译器特性)
int age = 10; @(age) 将age变量包装成NSNumber对象
NSDate
y年 M月 d日 m分 s秒
H(24)时 h(12)时