#import的用途:
1.#import的用途:
1> 跟#include一样,拷贝文件的内容
2> 可以自动防止文件的内容被重复拷贝
2.#import <Foundation/NSObjCRuntime.h>
NSObjCRuntime.h中有NSLog函数的声明
3.Foundation框架头文件的路径
1> 右击Xcode.app --> 显示包内容
2> Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/Foundation.framework
4.主头文件
1> 主头文件:最主要的头文件,名字一般跟框架名称一样,包含了框架中的所有其他头文件
2> Foundation框架的主头文件名称就是Foundation.h
3> 只需要包含Foundation框架主头文件,就可以使用整个框架的东西
5.运行过程
1> 编写OC源文件:.m、.c
2> 编译:cc -c xxx.m xxx.c
3> 链接:cc xxx.o xxx.o -framework Foundation
(只有用到了Foundation框架才需要加上-framework Foundation)
4> 运行:./a.out
类的设计
1.类的设计:
1> 类名
* 类名的第一个字母必须是大写
* 不能有下划线
* 多个英文单词,用驼峰标识
2> 属性
3> 行为(功能)
2.植物大战僵尸的僵尸
* 类名:Zoombie
* 属性:life、speed、gongjili
* 行为:walk、bite、die
3.雷电的飞机
* 类名:Plane
* 属性:life、gongjili、speed、bombCount
* 行为:fly、bomb、shoot、die
4.电脑
* 类名:Computer
* 属性:band、expireDate
* 行为:open、close
类的声明和实现
/*
类名:Car
属性:轮胎个数、时速(速度)
行为:跑
*/
// 因为使用了NSObject
// 完整地写一个函数:函数的声明和定义(实现)
// 完整地写一个类:类的声明和实现
// 1.类的声明
// 声明对象的属性、行为
// : NSObject 目的是:让Car这个类具备创建对象的能力
@interface Car : NSObject
{// 用来声明对象属性(实例变量\成员变量,默认会初始化为0)
// @public可以让外部的指针间接访问对象内部的成员变量
@public
int wheels; // 轮胎个数
int speed; // 时速(xxkm/h)
}
// 方法(行为):方法名、参数、返回值(声明、实现)
// 只要是OC对象的方法,必须以减号 - 开头
// OC方法中任何数据类型都必须用小括号()扩住
// OC方法中的小括号():括住数据类型
- (void)run;
@end
// 2.类的实现
// 用来实现@inteface中声明的方法
@implementation Car
// 方法的实现(说清楚方法里面有什么代码)
- (void)run
{
NSLog(@"车子跑起来了");
}
@end
// 在OC中,想执行一些行为,就写上一个中括号[行为执行者 行为名称]
// 利用类来创建对象
// 执行了Car这个类的new行为来创建新对象
// 定义了一个指针变量p,p将来指向的是Car类型的对象
// [Car new]每次都会创建出一个新对象,并且会返回新对象本身(新对象的地址)
// 在使用类创建对象之前,会将类加载进内存
常见错误
方法
1.对象方法都是以减号 -
2.对象方法的声明必须写在@interface和@end之间
对象方法的实现必须写在@implementation和@end之间
3.对象方法只能由对象来调用
4.对象方法归类\对象所有
函数
1.函数能写在文件中的任意位置(@interface和@end之间除外),函数归文件所有
2.函数调用不依赖于对象
3.函数内部不能直接通过成员变量名访问某个对象的成员变量
封装
set方法
1.作用: 提供一个方法给外界设置成员变量值,可以在方法里面对参数进行相应过滤
2.命名规范:
1> 方法名必须以set开头
2> set后面跟上成员变量的名称,成员变量的首字母必须大写
3> 返回值一定是void
4> 一定要接收一个参数,而且参数类型跟成员变量类型一致
5> 形参的名称不能跟成员变量名一样
get方法
1.作用:返回对象内部的成员变量
2.命名规范:
1> 肯定有返回值,返回值类型肯定与成员变量类型一致
2> 方法名跟成员变量名一样
3> 不需要接收任何参数
// 成员变量尽量不要用@public
// @public
int age;
//@public
// 只读(readonly):只允许外界访问我的no,不允许外界修改我的no
int no; // 只需要提供get方法
封装细节
成员变量的命名规范:一定要以下划线 _ 开头
作用:
1.让成员变量和get方法的名称区分开
2.可以跟局部变量区分开,一看到下划线开头的变量,一般都是成员变量
OC弱语法
尽管编译器容错能力比较,但是写代码必须规范
一旦运行过程中出错,就会闪退
/*
-[Person test]: unrecognized selector sent to instance 0x7fd2ea4097c0
给Person对象发送了一个不能识别的消息:test
*/
OC是在运行过程中才会检测对象有没有实现相应的方法
类方法
对象方法
1> 减号 - 开头
2> 只能由对象来调用
3> 对象方法中能访问当前对象的成员变量(实例变量)
类方法
1> 加号 + 开头
2> 只能由类(名)来调用
3> 类方法中不能访问成员变量(实例变量)
类方法的好处和使用场合
1> 不依赖于对象,执行效率高
2> 能用类方法,尽量用类方法
3> 场合:当方法内部不需要使用到成员变量时,就可以改为类方法
可以允许类方法和对象方法同名
// error:instance variable 'age' accessed in class method
// 实例变量age不能在类方法中访问
self
self的用途:
1> 谁调用了当前方法,self就代表谁
* self出现在对象方法中,self就代表对象
* self出现在类方法中,self就代表类
2> 在对象方法利用"self->成员变量名"访问当前对象内部的成员变量
2> [self 方法名]可以调用其他对象方法\类方法
继承
1.继承的好处:
1> 抽取重复代码
2> 建立了类之间的关系
3> 子类可以拥有父类中的所有成员变量和方法
2.注意点
1> 基本上所有类的根类是NSObject
1.重写:子类重新实现父类中的某个方法,覆盖父类以前的做法
2.注意
1> 父类必须声明在子类的前面
2> 子类不能拥有和父类相同的成员变量
3> 调用某个方法时,优先去当前类中找,如果找不到,去父类中找
2.坏处:耦合性太强
继承的使用场合
1> 当两个类拥有相同属性和方法的时候,就可以将相同的东西抽取到一个父类中
2> 当A类完全拥有B类中的部分属性和方法时,可以考虑让B类继承A类
super
super的作用
1.直接调用父类中的某个方法
2.super处在对象方法中,那么就会调用父类的对象方法
super处在类方法中,那么就会调用父类的类方法
3.使用场合:子类重写父类的方法时想保留父类的一些行为
多态
多态
1.没有继承就没有多态
2.代码的体现:父类类型的指针指向子类对象
3.好处:如果函数\方法参数中使用的是父类类型,可以传入父类、子类对象
4.局限性:
1> 父类类型的变量 不能 直接调用子类特有的方法。必须强转为子类类型变量后,才能直接调用子类特有的方法
// 多种形态
//Dog *d = [Dog new]; // Dog类型
// 多态:父类指针指向子类对象
Animal *a = [Dog new];
// 调用方法时会检测对象的真实形象
[a eat];
点语法
点语法的本质还是方法调用
成员变量的作用域
@public : 在任何地方都能直接访问对象的成员变量
@private : 只能在当前类的对象方法中直接访问(@implementation中默认是@private)
@protected : 可以在当前类及其子类的对象方法中直接访问 (@interface中默认就是@protected)
@package : 只要处在同一个框架中,就能直接访问对象的成员变量
@interface和@implementation中不能声明同名的成员变量
@property和@synthesize
@property:可以自动生成某个成员变量的setter和getter声明
@synthesize自动生成age的setter和getter实现,并且会访问_age这个成员变量
//@synthesize speed = _speed, wheels = _wheels;
// 会访问_speed这个成员变量,如果不存在,就会自动生成@private类型的_speed变量
// 默认会访问age这个成员变量,如果没有age,就会自动生成@private类型的age变量
@synthesize age;
id
万能指针,能指向\操作任何OC对象
构造方法
// 构造方法:用来初始化对象的方法,是个对象方法,-开头
// 重写构造方法的目的:为了让对象创建出来,成员变量就会有一些固定的值
/*
重写构造方法的注意点
1.先调用父类的构造方法([super init])
2.再进行子类内部成员变量的初始化
*/
/*
完整地创建一个可用的对象
1.分配存储空间 +alloc
2.初始化 -init
*/
/*
自定义构造方法的规范
1.一定是对象方法,一定以 - 开头
2.返回值一般是id类型
3.方法名一般以initWith开头
*/
// 父类的属性交给父类方法去处理,子类方法处理子类自己的属性
Category-分类
分类的作用:在不改变原来类内容的基础上,可以为类增加一些方法
使用注意:
1.分类只能增加方法,不能增加成员变量
2.分类方法实现中可以访问原来类中声明的成员变量
3.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用
4.方法调用的优先级:分类(最后参与编译的分类优先) --> 原来类 --> 父类
// 优先去分类中查找,然后再去原来类中找,最后再去父类中找
类的本质
1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法。只会调用一次。
2.当第一次使用某个类时,就会调用当前类的+initialize方法
3.先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load方法)
先初始化父类,再初始化子类(先调用父类的+initialize方法,再调用子类的+initialize方法)
// 类本身也是一个对象,是个Class类型的对象,简称类对象
/*
利用Class 创建 Person类对象
利用 Person类对象 创建 Person类型的对象
*/
description方法
决定了实例对象的输出结果
// 默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>
// 1.会调用对象p的-description方法
// 2.拿到-description方法的返回值(NSString *)显示到屏幕上
// 3.-description方法默认返回的是“类名+内存地址”
// 输出当前函数名
NSLog(@"%s\n", __func__);
// 输出行号
NSLog(@"%d", __LINE__);
// NSLog输出C语言字符串的时候,不能有中文
// NSLog(@"%s", __FILE__);
// 输出源文件的名称
printf("%s\n", __FILE__);
Person *p = [[Person alloc] init];
// 指针变量的地址
NSLog(@"%p", &p);
// 对象的地址
NSLog(@"%p", p);
// <类名:对象地址>
NSLog(@"%@", p);
SEL
/*
SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址。找到方法地址就可以调用方法
其实消息就是SEL
*/
int main() {
Person *p = [[Person alloc] init];
[p test2];
// NSString *name = @"test2";
//
// SEL s = NSSelectorFromString(name);
//
// [p performSelector:s];
// 间接调用test2方法
//[p performSelector:@selector(test2)];
//[p test3:@"123"];
// SEL s = @selector(test3:);
//
// [p performSelector:s withObject:@"456"];
//[p test2];
// 1.把test2包装成SEL类型的数据
// 2.根据SEL数据找到对应的方法地址
// 3.根据方法地址调用对应的方法
return 0;
}
- (void)test2 {
// _cmd代表着当前方法
NSString *str = NSStringFromSelector(_cmd);
// 会引发死循环
// [self performSelector:_cmd];
NSLog(@"调用了test2方法-----%@", str);
}
网友评论