1.点语法
点语法的本质还是方法调用
比如 p.age=10; 就是转换为 [p setAge:10];
int a = p.age 转换为 int a = [p age];
2.成员变量作用域
1.基本概念
局部变量、全局变量都有自己的作用域,成员变量也不例外
2.类型
>@private:只能在当前类的实现@implementation中直接访问
>@protected:可以在当前类以及子类的实现@implementation中直接访问
>@public:任何地方都可直接访问
>@package:同一个“体系内”(框架)可以访问,介于@private@public之间
3.继承补充
>专业术语 父类\超类 superclass 子类 subclass\subclasses 单继承
4.@implementation补充
没有@interface,只有@implementaton,也可以开发一个类,在.m文件进行声明
在声明里写的成员变量,默认是private
实现里面也能写成员变量 默认是私有,因为不会被其他文件包含,无法改设置就算写上@public也没用
(@interface)声明和(@implementation)实现里面不能定义同名的变量
5.成员变量的作用域
@public:在作何地方都能直接方问对象的成员变量
@private:只能在当前类的对象方法中直接访问(@implementation默认)
@protected:可以在当前类及其子类的对象方法中直接访问(@interface默认)
@package:只要处在同一个框架中,就能直接方问对象的成员变量
@interface与@implementation中的成员变量不能同名
子类与父类的成员变量不能同名
二.关键字
1.@property
在@interface和@end之间
编译器特性,当遇到@property int age自动生成某个成员变量的setter和getter声明,不能重复声明
如果使用了@property,但是没有成员变量,则自动生成private类型的成员变量
缺点,成员变量是死的,无法设置public或protected
2.@synthesize
在@implementation和@end之间
自动生用age的setter和getter的实现,并且会访问_age这个成员变量
@synthesize age = _age;也可以@synthesize age = _age,name = _name;
如果@synthesize age没有指定,那么默认访问和age一样的成员变量
3.@property新特性
自从Xcode4.4以后,@property就独揽了@synthesize的功能,也不是说,@property可以同时声明成员变量的声明和实现
@property(nonatomic,retain) UIWindow *window;
其中参数主要分为三类:
读写属性: (readwrite/readonly)
setter语意:(assign/retain/copy)
原子性: (atomicity/nonatomic)
4.各参数意义如下:
readwrite: 产生setter\getter方法
readonly: 只产生简单的getter,没有setter。
assign: 默认类型,setter方法直接赋值,而不进行retain操作
retain: setter方法对参数进行release旧值,再retain新值。
copy: setter方法进行Copy操作,与retain一样,不对旧值release
nonatomic: 禁止多线程,变量保护,提高性能
5.id
id是一种类似如 id d 所以变量名不能叫id,id是关键字
id是万能指针,能指向\操作任何OC对象
//id == NSObject *
typedef struct objc_object{
Class isa;
}*id;
//id后面不加*
用id调用不存在的方法会报错
三.类的构造方法
1.new的用处
完整地创建一个可用的对象
1.分配存储空间 + alloc
2.初始化 - init
3.释放 - dealloc
Person *p3 = [[Person alloc] init];
2.构造方法:是用来初始化对象的方法,是对象方法 -开头
重写构造方法的目的:为了让对象创建出来,成员变量就会有一些固定的值
重写构造方法注意点
1.先调用父类的构造方法([super init])
2.再进行子类内部成员变量的初始化
重写-init方法 - (id)init
1.一定要调用回super的init方法:初始化父类中声明的一些成员变量和其他属性 self = [super init];//当前对象 self = [父类将isa初始化为当前类]
2.当前对象指针指向父类构造出来的对象
3.当前对象指针 self = [父类将isa初始化为当前类]
init的执行过程,运行原理
//子类实例被创建的时候会先创建父类实例(调用父类的无参数构造方法),然后再创建子类实例
重写构造方法的执行顺序
student init super init
person init super init
nsobject init ....被封闭了
返回一个class
3.自定义构造函数规范
1.一定是对象方法,一定以 - 开头
2.返回值一般以id类型
3.方法名一般以initWith开头
四.类的扩展
Category分类 、类别、类目
使用分类扩展类,并且不修改类原来的代码
1.分类只能增加方法,不能增加成员变量,添加成员变量可以使用继承。
2.分类的方法实现中可以使用成员变量
3.分类的优先级最高,先去分类中找,再去原来的类中找,最后到父类中去找
4.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会有警告
5.多个分类实现的方法,最后一个编译的有效
//声明
@interface 类名(分类名称)
@end
//实现
@implementation 类名(分类名称)
@end
xcode添加分类->项目右键-NewFile OS X->Cocoa->Objective-C category(在Xcode6中已移除,如想使用需要手动添加模版文件)
xcode6 想要添加分类需要 项目右键-NewFile OS X->Cocoa->Objective-C File 然后在弹出窗口中的 File Type:Category然后输入其它之后添加。
扩展
Extension
作用:定义私有方法。可以隐藏不对外公布的方法。多用于隐藏一些中间步骤的方法。
写法:在.m文件中 @implementation 前实现
例如在 @implementation之前加上
@interface ViewController ()
{
int a;//默认私有
}
@end
协议
protocol
可以实现类似多继承的方法。一个类遵守多个协议<...,...>
在协议里声明方法来让类遵守
@required //默认是必须实现的
- (void)method1;
- (void)method2;
@optional //可以选择实现
五.类的深入研究
1.利用类需要先创建类类型对象
2.类需要占据存储空间。
3.每个类对象,占据不同的存储空间
4.类本身也是一个对像,是Class类型的对象
Class类型的定义
typedef struct objc_class *Class;
类名就代表着类对象,每个类只有一个类对象
内存中的类对象
Person * p = [[Person alloc] init];
获取内存中的类对象
1.Class c = [p class];
2.Class c2 = [Person class];
NSLog(@"c=%p,c2=%p",c,c2);
//类对象 == 类
Class c = [p class];
[c test];
Person *p2 = [[c new] init];
类的加载
1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法,只会调用一次.
2.当第一次使用某个类时,就会调用当前类的+initialize方法,但是会先找分类的,因为有优先级
3.加载优先级 顺序 先初始化父类-初始化子类
4.initialize调用优先级 先最后编译的分类-本类-父类
@implementation Person
+ (void)test
{
NSLog(@"test");
}
//当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法,加载顺序是先父类后子类
+ (void)load
{
NSLog(@"load");
}
//当第一次使用这个类的时候,就会调用一次initialize方法
//当分类和类都有initialize方法时,只会调用分类的initialize方法
+ (void)initialize
{
NSLog(@"initialize");
}
NSLog函数的工作原理
Person *p = [[Person alloc] init];
//默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>
//1.首先调用对象的-description方法
//2.拿到-description方法的返回值(NSString *)显示到屏幕上
//3.-description方法默认返回的是"类名+内存地址"
//子类会继承重写的方法
NSLog(@"%@",p);
Class c = [Person class];
//1.首先调用类的+description方法
//2.拿到+description方法的返回值(NSString *)显示到屏幕上
//3.+description方法默认返回的是"类名"
NSLog(@"%@",c);
重写-description方法
NSLog的其它功能
__func__ %s Current function signature.
__LINE__ %d Current line number in the source code file.
__FILE__ %s Full path to the source code file.
__PRETTY_FUNCTION__ %s Like__func__,but includes verbose type information in C++ code.s
SEL
每个SEL对应一个方法地址
每个类的方法列表都存储在类对象中
根据一个SEL对象就可以找到方法的地址,进而调用方法
SEL类型的定义
typedef struct objc_selector *SEL
SEL对象的创建
[p test:@"123"];
SEL s = @selector(test:);
NSString *name = @"test";
SEL s2 = NSSelectorFromString(@"test");
[p performSelector:s withObject:@"456"];
SEL对象的其他用法
//将 SEL对象转为NSString对象
NSString *str =NSStringFromSelector(@selector(test));
Person *p = [Person new]
//调用对象p的test方法
[p performSelector:@selector(test)];
_cmd
//每个方法内部都有一个_cmd,_cmd代表当前方法
用法
NSString *str = NSStringFromSelector(_cmd);
NSLog(@"调用了方法%@",str);
//死循环
[self performSelector:_cmd];
1.点语法
点语法的本质还是方法调用
比如 p.age=10; 就是转换为 [p setAge:10];
int a = p.age 转换为 int a = [p age];
xcode - Create a new Xcode project - OS X- Application - Command Line Tool - create
添加类
OS X - Cocoa(Source) - Objective-C class -> Class:Person ->Subclass of :NSObject -> Language:Objective-C - Create
生成Person.h与.m
command+r 运行
xcode6 需要点 右下角的[ |]图标(Hide the Console)才能看到控制台
Person.h->
//
// person.h
// oc06核心语法
//
// Created by Whome on 14-10-27.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
int _age;
NSString * _name;
}
- (void) setAge:(int)age;
- (int) age;
- (void) setName:(NSString *)name;
- (NSString *) name;
@end
Person.m->
//
// person.m
// oc06核心语法
//
// Created by Whome on 14-10-27.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Person.h"
@implementation Person
- (void) setAge:(int)age
{
//self.age = 10; 是死循环
_age = age;
NSLog(@"setAge:%d",self->_age);
}
- (int) age
{
NSLog(@"age:%d",self->_age);
return self->_age;
}
- (void) setName:(NSString *)name
{
_name = name;
NSLog(@"setName:%@",self->_name);
}
- (NSString *) name
{
NSLog(@"name:%@",self->_name);
return self->_name;
}
@end
main.m->
//
// main.m
// oc06核心语法
//
// Created by Whome on 14-10-27.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Person.h"
/*
点语法的本质还是方法调用
比如 p.age=10; 就是转换为 [p setAge:10];
int a = p.age 转换为 int a = [p age];
*/
int main() {
Person *p = [Person new];
p.age = 10;
int a = p.age;
NSLog(@"a:%d",a);
p.name = @"is name";
NSString *s = p.name;
NSLog(@"s:%@",s);
return 0;
}
控制台输出
2014-10-27 06:42:57.278 oc06核心语法[502:303] setAge:10
2014-10-27 06:42:57.287 oc06核心语法[502:303] age:10
2014-10-27 06:42:57.288 oc06核心语法[502:303] a:10
2014-10-27 06:42:57.294 oc06核心语法[502:303] setName:is name
2014-10-27 06:42:57.302 oc06核心语法[502:303] name:is name
2014-10-27 06:42:57.303 oc06核心语法[502:303] s:is name
Program ended with exit code: 0
2.成员变量作用域
1.基本概念
局部变量、全局变量都有自己的作用域,成员变量也不例外
2.类型
>@private:只能在当前类的实现@implementation中直接访问
>@protected:可以在当前类以及子类的实现@implementation中直接访问
>@public:任何地方都可直接访问
>@package:同一个“体系内”(框架)可以访问,介于@private@public之间
3.继承补充
>专业术语 父类\超类 superclass 子类 subclass\subclasses 单继承
4.@implementation补充
没有@interface,只有@implementaton,也可以开发一个类,在.m文件进行声明
在声明里写的成员变量,默认是private
实现里面也能写成员变量 默认是私有,因为不会被其他文件包含,无法改设置就算写上@public也没用
(@interface)声明和(@implementation)实现里面不能定义同名的变量
添加类
OS X - Cocoa(Source) - Objective-C class -> Class:Person ->Subclass of :NSObject -> Language:Objective-C - Create
生成Person.h与.m
Person.h->
//
// Person.h
// 02成员变量的作用域
//
// Created by Whome on 14-10-28.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
int _no;
@public //在作何地方都能直接方问对象的成员变量
int _age;
@private//只能在当前类的对象方法中直接访问
int _height;
@protected //可以在当前类及其子类的对象方法中直接访问
int _weight;
}
- (void)setHeight:(int) height;
- (int)height;
- (void)test;
@end
Person.m->
//
// Person.m
// 02成员变量的作用域
//
// Created by Whome on 14-10-28.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Person.h"
@implementation Person
{
int _aaa;//默认就是私有
@public
int _bbb;
}
- (void)test
{
_age = 19;
_height = 20;
_weight = 50;
_aaa = 10;
}
- (void)setHeight:(int)height{
_height = height;
}
- (int)height
{
return _height;
}
@end
Student.h->
//
// Student.h
// 02成员变量的作用域
//
// Created by Whome on 14-10-28.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Person.h"
@interface Student : Person
- (void)study;
@end
Student.m->
//
// Student.m
// 02成员变量的作用域
//
// Created by Whome on 14-10-28.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Student.h"
@implementation Student
- (void)study
{
//_height = 10;
[self setHeight:10];
int h = [self height];
_weight = 100;
NSLog(@"h:%d",h);
}
@end
Car.m->
//
// <pre name="code" class="objc">Car .m
// 02成员变量的作用域
//
// Created by Whome on 14-10-28.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Person.h"
#import "Student.h"
@implementation Car : NSObject
{
@public
int _speed;
}
- (void)setSpeed:(int)speed
{
_speed = speed;
}
- (int)speed
{
return _speed;
}
- (void) test{
NSLog(@"speed:%d",_speed);
}
@end
main.m->
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [Student new];
[stu setHeight:10];
NSLog(@"%d",[stu height]);
Car *c = [Car new];
c->_speed = 250;
c.speed = 10;
NSLog(@"speed:%d",c.speed);
Person *p = [Person new];
//私有的private
// p->_bbb = 10;
p->_age = 100;
//私有的private
// p->_height = 20;
}
return 0;
}
控制台:
2014-10-28 07:55:13.150 02成员变量的作用域[630:303] 10
2014-10-28 07:55:13.153 02成员变量的作用域[630:303] speed:10
Program ended with exit code: 0
1.@property
在@interface和@end之间
编译器特性,当遇到@property int age自动生成某个成员变量的setter和getter声明,不能重复声明
如果使用了@property,但是没有成员变量,则自动生成private类型的成员变量
缺点,成员变量是死的,无法设置public或protected
2.@synthesize
在@implementation和@end之间
自动生用age的setter和getter的实现,并且会访问_age这个成员变量
@synthesize age = _age;也可以@synthesize age = _age,name = _name;
如果@synthesize age没有指定,那么默认访问和age一样的成员变量
@property新特性
自从Xcode4.4以后,@property就独揽了@synthesize的功能,也不是说,@property可以同时声明成员变量的声明和实现
@property(nonatomic,retain) UIWindow *window;
其中参数主要分为三类:
读写属性: (readwrite/readonly)
setter语意:(assign/retain/copy)
原子性: (atomicity/nonatomic)
各参数意义如下:
readwrite: 产生setter\getter方法
readonly: 只产生简单的getter,没有setter。
assign: 默认类型,setter方法直接赋值,而不进行retain操作
retain: setter方法对参数进行release旧值,再retain新值。
copy: setter方法进行Copy操作,与retain一样,不对旧值release
nonatomic: 禁止多线程,变量保护,提高性能
3.id
id是一种类似如 id d 所以变量名不能叫id,id是关键字
id是万能指针,能指向\操作任何OC对象
//id == NSObject *
typedef struct objc_object{
Class isa;
}*id;
//id后面不加*
用id调用不存在的方法会报错
Person.h->
//
// Person.h
// 03关键字
//
// Created by Whome on 14-10-28.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
int _age;
@private
int _width;
}
//自动生成getter和setter及private类型的成员变量
@property int height;
@property NSString* name;
//自动生成getter和setter
@property int age;
- (void)setWidth:(int)width;
- (int)width;
@end
Person.m->
//
// Person.m
// 03关键字
//
// Created by Whome on 14-10-28.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Person.h"
@implementation Person
//自动实现setter和getter
@synthesize height = _height;
//没有@property int width会报错
//@synthesize width = _widht;
- (void)setWidth:(int)width{
_width = width;
}
- (int)width{
return _width;
}
@end
Student.h->
//
// Student.h
// 03关键字
//
// Created by Whome on 14-10-28.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Person.h"
@interface Student : Person
@end
Student.m->
//
// Student.m
// 03关键字
//
// Created by Whome on 14-10-28.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Student.h"
@implementation Student
@end
main.m->
//
// main.m
// 03关键字
//
// Created by Whome on 14-10-28.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
Person *p = [Person new];
//只手动声明了成员变量
p.age = 10;
//纯@property
p.height=10;
//纯@property
p.name = @"is name";
//纯手动
p.width = 10;
NSLog(@"p.age:%d",p.age);
NSLog(@"p.height:%d",p.height);
NSLog(@"p.name:%@",p.name);
NSLog(@"p.width:%d",p.width);
Student *s = [Student new];
//父类的成员变量是私有的无法->调用,但是setter/getter可以用
//只手动声明了成员变量
s.age = 10;
//纯@property
s.height=10;
//纯@property
s.name = @"is name";
//纯手动
s.width = 10;
NSLog(@"s.age:%d",s.age);
NSLog(@"s.height:%d",s.height);
NSLog(@"s.name:%@",s.name);
NSLog(@"s.width:%d",s.width);
//id是一种类似如 id d 所以变量名不能叫id,id是关键字
//id是万能指针,能指向\操作任何OC对象
id d = [Student new];
//无法直接用.语法来调用
//d.age = 10;
[d setAge:10];
NSLog(@"d.age:%d",[d age]);
}
return 0;
}
控制台输出:
2014-10-29 00:10:15.692 03关键字[690:303] Hello, World!
2014-10-29 00:10:15.695 03关键字[690:303] p.age:10
2014-10-29 00:10:15.696 03关键字[690:303] p.height:10
2014-10-29 00:10:15.697 03关键字[690:303] p.name:is name
2014-10-29 00:10:15.698 03关键字[690:303] p.width:10
2014-10-29 00:10:15.699 03关键字[690:303] s.age:10
2014-10-29 00:10:15.700 03关键字[690:303] s.height:10
2014-10-29 00:10:15.701 03关键字[690:303] s.name:is name
2014-10-29 00:10:15.706 03关键字[690:303] s.width:10
2014-10-29 00:10:15.706 03关键字[690:303] d.age:10
1.new的用处
完整地创建一个可用的对象
1.分配存储空间 +alloc
2.初始化 -init
3.释放 -dealloc
Person *p3 = [[Person alloc] init];
//构造方法:是用来初始化对象的方法,是对象方法 -开头
//重写构造方法的目的:为了让对象创建出来,成员变量就会有一些固定的值
重写构造方法注意点
1.先调用父类的构造方法([super init])
2.再进行子类内部成员变量的初始化
//重写-init方法 - (id)init
//1.一定要调用回super的init方法:初始化父类中声明的一些成员变量和其他属性 self = [super init];//当前对象 self = [父类将isa初始化为当前类]
init的执行过程,运行原理
//子类实例被创建的时候会先创建父类实例(调用父类的无参数构造方法),然后再创建子类实例
自定义构造函数规范
1.一定是对象方法,一定以 - 开头
2.返回值一般以id类型
3.方法名一般以initWith开头
Person.h->
//
// Person.h
// 04构造方法
//
// Created by Whome on 14-10-29.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
// int _age;
// NSString* _name;
}
@property int age;
@property NSString* name;
- (id)init;
/*
自定义构造函数规范
1.一定是对象方法,一定以 - 开头
2.返回值一般以id类型
3.方法名一般以initWith开头
*/
- (id)initWithName:(NSString *) name andAge:(int) age;
@end
Person.m->
//
// Person.m
// 04构造方法
//
// Created by Whome on 14-10-29.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Person.h"
@implementation Person
//重写重构方法
- (id)init
{
//当前对象指针指向父类构造出来的对象
//当前对象指针 self = [父类将isa初始化为当前类]
self = [super init];
//如果self不为0
if(self){
//初始化成员变量
_age = 10;
}
return self;
}
- (id)initWithName:(NSString *)name andAge:(int)age
{
if(self = [super init])
{
_name = name;
_age = age;
}
return self;
}
@end
Student.h->
//
// Student.h
// 04构造方法
//
// Created by Whome on 14-10-29.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Person.h"
@interface Student : Person
{
int _no;
}
@property int no;
- (id)init;
- (id)initWithName:(NSString *) name andAge:(int) age andNo:(int) no;
@end
Student.m->
//
// Student.m
// 04构造方法
//
// Created by Whome on 14-10-29.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Student.h"
@implementation Student
//重写重构方法
- (id)init
{
//当前对象指针指向父类构造出来的对象
//当前对象指针 self = [父类将isa初始化为当前类]
//一定要调用父类的init方法,初始化一些父类声明的成员变量和方法
self = [super init];
//如果self不为0,如果对象初始化成功,能有必要进行接下来的初始化
if(self){
//初始化成员变量
_no = 50;
}
//返回一个已初始化完毕的对象
return self;
}
- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no
{
if(self = [super initWithName:name andAge:age ])
{
//如果_name是子类可继随的那么
//_name = name;
//如果_name是私有的那么
//self.name = name;
//如果想在父类构造时就初始化,而且并不关心父类的成员变量那么
//[super initWithName:name andAge:age ]
_no = no;
}
return self;
}
@end
main.m->
//
// main.m
// 04构造方法
//
// Created by Whome on 14-10-29.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
/*
1.new的用处
完整地创建一个可用的对象
1.分配存储空间 +alloc
2.初始化 -init
*/
Student *s = [[Student alloc] init];
NSLog(@"age:%d,no:%d",s.age,s.no);
/*
重写构造方法的执行顺序
student init super init
person init super init
nsobject init ....被封闭了
返回一个class
*/
/*
自定义构造函数规范
1.一定是对象方法,一定以 - 开头
2.返回值一般以id类型
3.方法名一般以initWith开头
*/
s = [[Student alloc] initWithName:@"is name" andAge:10 andNo: 20];
NSLog(@"age:%d,no:%d,name:%@",s.age,s.no,s.name);
}
return 0;
}
控制台->
2014-10-30 06:24:35.326 04构造方法[398:303] Hello, World!
2014-10-30 06:24:35.332 04构造方法[398:303] age:10,no:50
2014-10-30 06:24:35.336 04构造方法[398:303] age:10,no:20,name:is name
Category分类 、类别、类目
/*使用分类扩展类,并且不修改类原来的代码
1.分类只能增加方法,不能增加成员变量,添加成员变量可以使用继承。
2.分类的方法实现中可以使用成员变量
3.分类的优先级最高,先去分类中找,再去原来的类中找,最后到父类中去找
4.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会有警告
5.多个分类实现的方法,最后一个编译的有效
*/
//声明
@interface 类名(分类名称)
@end
//实现
@implementation 类名(分类名称)
@end
xcode添加分类->项目右键-NewFile OS X->Cocoa->Objective-C category(在Xcode6中已移除,如想使用需要手动添加模版文件)
xcode6 想要添加分类需要 项目右键-NewFile OS X->Cocoa->Objective-C File 然后在弹出窗口中的 File Type:Category然后输入其它之后添加。
扩展
Extension
作用:定义私有方法。可以隐藏不对外公布的方法。多用于隐藏一些中间步骤的方法。
写法:在.m文件中 @implementation 前实现
例如在 @implementation之前加上
@interface ViewController ()
{
int a;//默认私有
}
@end
协议
protocol
可以实现类似多继承的方法。一个类遵守多个协议<...,...>
在协议里声明方法来让类遵守
@required //默认是必须实现的
- (void)method1;
- (void)method2;
@optional //可以选择实现
给系统自带类添加分类
1.给NSString增加一个类方法:计算某个字符串中阿拉伯数字的个数
2.给NSString增加一个对象方法:计算当前字符串中阿拉伯数字的个数
Person.h->
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
//@private
//int _age;
}
@property int age;
- (void)test;
@end
Person.m->
#import "Person.h"
@implementation Person
- (void)test
{
NSLog(@"person");
}
@end
Person+sum.h->
#import "Person.h"
@interface Person (sum)
- (int)sumWithAge:(int)age1 andAge2:(int)age2;
- (void)test;
@end
Person+sum.m->
#import "Person+sum.h"
@implementation Person (sum)
- (int)sumWithAge:(int)age1 andAge2:(int)age2
{
self.age = age2+age2;
NSLog(@"self.age:%d",self.age);
//出错,因为没有在类声明变量。
//_age = 0;
//age = 0;
//self->_age=0;
//self->age =0;
/*
self->age =age+age2;
return self->_age;
*/
return self.age;
}
- (void)test
{
NSLog(@"person+sum");
}
@end
NSString+count.h->
#import <Foundation/Foundation.h>
/*
给系统自带类添加分类
1.给NSString增加一个类方法:计算某个字符串中阿拉伯数字的个数
2.给NSString增加一个对象方法:计算当前字符串中阿拉伯数字的个数
*/
@interface NSString (count)
+ (int) numberCountOfString:(NSString *)str;
- (int)numberCount;
@end
NSString+count.m->
#import "NSString+count.h"
@implementation NSString (count)
// 计算某个字符串中阿拉伯数字的个数
+ (int) numberCountOfString:(NSString *)str
{
int count=0;
//获取长度并循环
for(int i=0;i<str.length;i++)
{
//获取字符
unichar c = [str characterAtIndex:i];
//将数字挑选出来
if(c>'0' && c<'9')
{
count++;
NSLog(@"%c",c);
}
}
return count;
}
//计算当前字符串中阿拉伯数字的个数
- (int)numberCount
{
int count=0;
for(int i=0;i<self.length;i++)
{
unichar c = [self characterAtIndex:i];
if(c>'0' && c<'9')
{
count++;
NSLog(@"%c",c);
}
}
return count;
}
@end
main.m->
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Person+sum.h"
#import "NSString+count.h"
/*
Category分类 、类别、类目
使用分类扩展类,并且不修改类原来的代码
1.分类只能增加方法,不能增加成员变量,添加成员变量可以使用继承。
2.分类的方法实现中可以使用成员变量
3.分类的优先级最高,先去分类中找,再去原来的类中找,最后到父类中去找
4.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会有警告
5.多个分类实现的方法,最后一个编译的有效
*/
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
Person *p = [[Person alloc]init];
int age = [p sumWithAge:10 andAge2:20];
NSLog(@"age:%d",age);
[p test];
int count = [NSString numberCountOfString:@"a12d5x778" ];
NSLog(@"count:%d",count);
NSString *s = @"ab234x278";
count = [s numberCount];
NSLog(@"count:%d",count);
count = [@"asdf2ssdf2" numberCount];
NSLog(@"count:%d",count);
}
return 0;
}
控制台:
2014-11-09 19:43:05.706 05类的分类[852:303] Hello, World!
2014-11-09 19:43:05.709 05类的分类[852:303] self.age:40
2014-11-09 19:43:05.710 05类的分类[852:303] age:40
2014-11-09 19:43:05.710 05类的分类[852:303] person+sum
2014-11-09 19:43:05.711 05类的分类[852:303] 1
2014-11-09 19:43:05.711 05类的分类[852:303] 2
2014-11-09 19:43:05.712 05类的分类[852:303] 5
2014-11-09 19:43:05.712 05类的分类[852:303] 7
2014-11-09 19:43:05.714 05类的分类[852:303] 7
2014-11-09 19:43:05.714 05类的分类[852:303] 8
2014-11-09 19:43:05.715 05类的分类[852:303] count:6
2014-11-09 19:43:05.717 05类的分类[852:303] 2
2014-11-09 19:43:05.718 05类的分类[852:303] 3
2014-11-09 19:43:05.719 05类的分类[852:303] 4
2014-11-09 19:43:05.719 05类的分类[852:303] 2
2014-11-09 19:43:05.720 05类的分类[852:303] 7
2014-11-09 19:43:05.721 05类的分类[852:303] 8
2014-11-09 19:43:05.721 05类的分类[852:303] count:6
2014-11-09 19:43:05.722 05类的分类[852:303] 2
2014-11-09 19:43:05.723 05类的分类[852:303] 2
2014-11-09 19:43:05.727 05类的分类[852:303] count:2
可能性
我觉得跟权限没关系应该是分类在调用这个变量的时候property还没有生成
那么就只有一个原因, 分类可以重写setAge和(get)age方法.
又因为set get 这两个方法一旦重写 那property 就不在会生成_age变量;
所以OC为了避免这种情况 直接限制在有分类的情况 必须自己书写变量 不能用property 这样就不会导致重写set get会让property失效了
你可以在正常情况下 写了property 然后在重写set get方法 你会发现找不到变量
类的深入研究
1.利用类需要先创建类类型对象
2.类需要占据存储空间。
3.每个类对象,占据不同的存储空间
4.类本身也是一个对像,是Class类型的对象
Class类型的定义
typedef struct objc_class *Class;
类名就代表着类对象,每个类只有一个类对象
内存中的类对象
Person * p = [[Person alloc] init];
获取内存中的类对象
1.Class c = [p class];
2.Class c2 = [Person class];
NSLog(@"c=%p,c2=%p",c,c2);
//类对象 == 类
Class c = [p class];
[c test];
Person *p2 = [[c new] init];
类的加载
1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法,只会调用一次.
2.当第一次使用某个类时,就会调用当前类的+initialize方法,但是会先找分类的,因为有优先级
3.加载优先级 顺序 先初始化父类-初始化子类
4.initialize调用优先级 先最后编译的分类-本类-父类
@implementation Person
+ (void)test
{
NSLog(@"test");
}
//当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法,加载顺序是先父类后子类
+ (void)load
{
NSLog(@"load");
}
//当第一次使用这个类的时候,就会调用一次initialize方法
//当分类和类都有initialize方法时,只会调用分类的initialize方法
+ (void)initialize
{
NSLog(@"initialize");
}
NSLog函数的工作原理
Person *p = [[Person alloc] init];
//默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>
//1.首先调用对象的-description方法
//2.拿到-description方法的返回值(NSString *)显示到屏幕上
//3.-description方法默认返回的是"类名+内存地址"
//子类会继承重写的方法
NSLog(@"%@",p);
Class c = [Person class];
//1.首先调用类的+description方法
//2.拿到+description方法的返回值(NSString *)显示到屏幕上
//3.+description方法默认返回的是"类名"
NSLog(@"%@",c);
重写-description方法
NSLog的其它功能
__func__ %s Current function signature.
__LINE__ %d Current line number in the source code file.
__FILE__ %s Full path to the source code file.
__PRETTY_FUNCTION__ %s Like__func__,but includes verbose type information in C++ code.s
SEL
每个SEL对应一个方法地址
每个类的方法列表都存储在类对象中
根据一个SEL对象就可以找到方法的地址,进而调用方法
SEL类型的定义
typedef struct objc_selector *SEL
SEL对象的创建
[p test:@"123"];
SEL s = @selector(test:);
NSString *name = @"test";
SEL s2 = NSSelectorFromString(@"test");
[p performSelector:s withObject:@"456"];
SEL对象的其他用法
//将 SEL对象转为NSString对象
NSString *str =NSStringFromSelector(@selector(test));
Person *p = [Person new]
//调用对象p的test方法
[p performSelector:@selector(test)];
_cmd
//每个方法内部都有一个_cmd,_cmd代表当前方法
用法
NSString *str = NSStringFromSelector(_cmd);
NSLog(@"调用了方法%@",str);
//死循环
[self performSelector:_cmd];
main.m->
//
// main.m
// 06类的深入
//
// Created by Whome on 14-11-7.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
/*
Class类型的定义
typedef struct objc_class *Class;
类名就代表着类对象,每个类只有一个类对象
*/
//获取内存中的类对象
Person *p = [[Person alloc]init];
Class c = [p class];
Class c1 = [Person class];
NSLog(@"c=%p,c1=%p",c,c1);
//类对象 == 类
Class c2 = [p class];
[c test];
Person *p1 = [[c2 new] init];
[p1 test];
/*
类的加载
1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法,只会调用一次.
2.当第一次使用某个类时,就会调用当前类的+initialize方法,但是会先找分类的,因为有优先级
3.加载优先级 顺序 先初始化父类-初始化子类
4.initialize调用优先级 先最后编译的分类-本类-父类
*/
/*
%@ 对象
%d, %i 整数
%u,%z 无符整形
%f 浮点/双字
%x, %X 十六进制整数
%o 八进制整数
%zu size_t
%p 指针
%e 浮点/双字 (科学计算)
%g 浮点/双字
%s C字符串
%.*s Pascal字符串
%c 字符
%C unichar
%lld 64位长整数(long long)
%llu 无符64位长整数
%Lf 64位双字
%hhd BOOL布尔类型
__func__ %s Current function signature.
__LINE__ %d Current line number in the source code file.
__FILE__ %s Full path to the source code file.
__PRETTY_FUNCTION__ %s Like__func__,but includes verbose type information in C++ code.s
*/
NSLog(@"__func__:%s",__func__);
NSLog(@"__LINE__:%d",__LINE__);
NSLog(@"__FILE__:%s",__FILE__);
NSLog(@"__PRETTY_FUNCTION__:%s",__PRETTY_FUNCTION__);
// NSLog函数的工作原理
Student *p2 = [[Student alloc] init];
//默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>
//1.首先调用对象的-description方法
//2.拿到-description方法的返回值(NSString *)显示到屏幕上
//3.-description方法默认返回的是"类名+内存地址"
NSLog(@"p2:%@",p2);
Class c3 = [Student class];
//1.首先调用类的+description方法
//2.拿到+description方法的返回值(NSString *)显示到屏幕上
//3.+description方法默认返回的是"类名"
NSLog(@"c3:%@",c3);
//重写-description方法
/*
SEL
每个SEL对应一个方法地址
每个类的方法列表都存储在类对象中
根据一个SEL对象就可以找到方法的地址,进而调用方法
SEL类型的定义
typedef struct objc_selector *SEL
*/
//SEL对象的创建
[p testStr:@"123"];
SEL s = @selector(testStr:);
NSString *name = @"testStr:";
//SEL s2 = NSSelectorFromString(@"testStr");
SEL s2 = NSSelectorFromString(name);
[p performSelector:s withObject:@"456"];
[p performSelector:s2 withObject:@"56"];
//SEL对象的其他用法
//将 SEL对象转为NSString对象
NSString *str =NSStringFromSelector(@selector(test));
NSLog(@"str%@:",str);
//间接调用对象p的test方法
s = @selector(test);
[p performSelector:s];
//不知道为什么调用test报错
/*
保留test对象方法在编辑时报Multiple methods named 'test' found with mismatched result,parameter type or attributes
只保类test类方法在编译时报*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person test]: unrecognized selector sent to instance 0x100104fd0'
*/
[p performSelector:@selector(test2)];
}
return 0;
}
Person.h->
//
// Person.h
// 06类的深入
//
// Created by Whome on 14-11-7.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Person : NSObject
+ (void)test;
- (void)test;
- (void)test2;
- (void)testStr:(NSString*)str;
@end
Person.m->
//
// Person.m
// 06类的深入
//
// Created by Whome on 14-11-7.
// Copyright (c) 2014年 Whome. All rights reserved.
//
#import "Person.h"
@implementation Person
+ (void)test
{
NSLog(@"Person test");
}
- (void)test
{
NSLog(@"Person -test");
/*
_cmd
每个方法内部都有一个_cmd,_cmd代表当前方法
用法
*/
NSString *str = NSStringFromSelector(_cmd);
NSLog(@"调用了方法%@",str);
//死循环
//[self performSelector:_cmd];
}
//当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法,加载顺序是先父类后子类
+ (void)load
{
NSLog(@"Person load");
}
//当第一次使用这个类的时候,就会调用一次initialize方法
//当分类和类都有initialize方法时,只会调用分类的initialize方法
+ (void)initialize
{
NSLog(@"Person initialize");
}
//默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>
//1.首先调用对象的-description方法
//2.拿到-description方法的返回值(NSString *)显示到屏幕上
//3.-description方法默认返回的是"类名+内存地址"
//子类会继承这个方法
- (NSString*)description
{
return @"NSLog description 返回值";
}
- (void)testStr:(NSString*)str{
NSLog(@"%@",str);
}
- (void)test2{
NSLog(@"- test2");
NSString *str = NSStringFromSelector(_cmd);
NSLog(@"调用了方法%@",str);
}
+ (void)test2{
NSLog(@"+ test2");
}
@end
Person+Mj.h->
#import "Person.h"
@interface Person (MJ)
@end
Person+Mj.m->
#import "Person+MJ.h"
@implementation Person (MJ)
//当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法,加载顺序是先父类后子类
+ (void)load
{
NSLog(@"Person+MJ load");
}
//当第一次使用这个类的时候,就会调用一次initialize方法
//当分类和类都有initialize方法时,只会调用分类的initialize方法
+ (void)initialize
{
NSLog(@"Person+MJ initialize");
}
@end
Student.h->
#import "Person.h"
@interface Student : Person
+ (void)test;
@end
Student.m->
#import "Student.h"
@implementation Student
+ (void)test
{
NSLog(@"Student test");
}
//当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法,加载顺序是先父类后子类
+ (void)load
{
NSLog(@"Student load");
}
//当第一次使用这个类的时候,就会调用一次initialize方法
//当分类和类都有initialize方法时,只会调用分类的initialize方法
+ (void)initialize
{
NSLog(@"Student initialize");
}
@end
控制台
2014-11-09 07:41:25.908 06类的深入[894:303] Person load
2014-11-09 07:41:25.911 06类的深入[894:303] Student load
2014-11-09 07:41:25.912 06类的深入[894:303] Person+MJ load
2014-11-09 07:41:25.913 06类的深入[894:303] Hello, World!
2014-11-09 07:41:25.913 06类的深入[894:303] Person+MJ initialize
2014-11-09 07:41:25.914 06类的深入[894:303] c=0x1000026d0,c1=0x1000026d0
2014-11-09 07:41:25.915 06类的深入[894:303] Person test
2014-11-09 07:41:25.916 06类的深入[894:303] - test2
2014-11-09 07:41:25.916 06类的深入[894:303] 调用了方法test2
2014-11-09 07:41:25.919 06类的深入[894:303] __func__:main
2014-11-09 07:41:25.919 06类的深入[894:303] __LINE__:73
2014-11-09 07:41:25.921 06类的深入[894:303] __FILE__:
2014-11-09 07:41:25.921 06类的深入[894:303] __PRETTY_FUNCTION__:int main(int, const char **)
2014-11-09 07:41:25.922 06类的深入[894:303] Student initialize
2014-11-09 07:41:25.923 06类的深入[894:303] p2:NSLog description 返回值
2014-11-09 07:41:25.924 06类的深入[894:303] c3:Student
2014-11-09 07:41:25.925 06类的深入[894:303] 123
2014-11-09 07:41:25.925 06类的深入[894:303] strtest:
Program ended with exit code: 0
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------