【IOS学习基础】OC类的相关

几天前突然在别人的类的.m文件中看到这么一句代码:@synthesize xxxx = _xxxx; 当时愣是没理解啥意思,过后才缓过神来发现原来是把一些类的基础知识忘记了,虽然不用过多去深究以前的一些旧东西,但但是既然遇到了,还是复习一下。

一、类与对象

  1.类:类是定义同一类所有属性和方法的蓝图或原型。

  2.对象:用来描述客观事物的一个实体,由具体的属性和方法构成。

  3.类与对象的关系:类是用来制作无数实体(对象)的工程图纸。

  4.类的特征:属性

  5.类的行为:方法

二、封装

  1.类就是封装,封装了属性与方法。它是一种思想,其核心就是“暴露出必要的内容给外部用属性方法私有,而对于内部细节,使用者不用去关心”。

  我们声明一个Teacher类:

#import <Foundation/Foundation.h>
@interface Teacher : NSObject
{  // @public 公共的
    NSString *_name;  //成员变量(实例变量) 默认@protected修饰(受保护的)

  /* 顺带一提:@protected、@public、@private、@package   * @public 成员变量可以被在任何地方访问。   * @protected 成员变量能被声明它的类和子类访问(默认)   * @private 成员变量只能在声明它的类中访问(默认现在用@Property关键字生成的成员变量是这个)   * @package 一个@package成员变量在实现这个类的可执行文件镜像中实际上是@public的,但是在外面就是@private。   */
}@end

   然而,我们此时在外面根本无法访问到这个受保护的成员变量。

  访问该成员变量的方法:

  1> 打开上面 “@public”的注释,在外面以"->“方式访问

Teacher *tec = [[Teacher alloc] init];
tec->_name = @"姓名";

  2> 封装set、get方法进行间接访问,在.h文件中加上方法声明,在.m文件中实现

-(void)setName:(NSString *)name;
-(NSString *)getName;

-(void)setName:(NSString *)name
{
    _name = name;
}
-(NSString *)getName
{
    return _name;
}

  这样,便可以在外面通过调用方法的方式来间接访问该成员变量,其实下面讲的@property关键字就是帮你解决了这个set、get方法的访问

让其在外面以"."的方式(本质上是调用set和get方法)间接访问。

Teacher *tec = [[Teacher alloc] init];
[tec setName:@"testName"];
NSLog(@"%@",[tec getName]);

  总结一下成员变量和成员属性:

    ①成员变量即实例变量

    ②成员属性用于间接访问类中的成员变量

    ③假如你想让类的一个特性私有,只在本类访问,就定义成成员变量;假如你想在类之外访问该类的一个特性,你就将其定义成成员属性。  

三、@property和@synthesize关键字

  1.在以前,我们经常可以看到这种类的声明

#import <Foundation/Foundation.h>

@interface Teacher : NSObject
{
    NSString *_name; //成员变量
}
@property (nonatomic,copy)NSString *name;  //成员属性

@end

#import "Teacher.h"

@implementation Teacher

@synthesize name = _name;

@end

  这是在之前的编译器特性中:

  @property帮我们声明了name属性的set和get方法

  @synthesize name = _name则表示对@property声明的属性name实现set和get方法,后面的” = _name“表示实现的时候访问成员变量_name

  2.而现在我们为类声明一个属性则通常只需要一句话(我一般都用这种,方便):

@property (nonatomic,copy)NSString *name; //这里指在.h文件中声明

  这是在新的编译器特性中:

  @property直接帮助我们干了三件事:

    ①当该name属性的成员变量未被指定时,会生成一个默认为_name的成员变量,但是该属性为@private(私有的),自己写的则是@protected(受保护的)

    ②声明name属性的set、get方法

    ③实现name属性的set、get方法

  注:如果你想要用自己生成的成员变量,则用之前的那种格式,在.h文件中增加一个成员变量,在.m文件中加上@synasize xxx = _xxx。

  成员变量用”_“下划线开头命名的好处:

    ①区分成员变量和属性

    ②防止局部变量和成员变量命名冲突

四、关于继承的几句话

  1.继承:建立类之间的关系,实现代码的重用性,方便系统扩展。

  2.继承是为了避免多个相似的类中相同的成员变量反复定义而延伸的一个特性。

  3.继承有单根性与传递性

  4.被继承的类称之为父类或基类,继承的类称之为子类或派生类

  5.super关键字:self用于访问本类成员,而super则是用于在子类中访问父类成员 

五、分类catagory

  分类:①在不改变原来类的基础上,为类增加一些方法,来对类进行一个扩展。

     ②在开发中,我们一般对都是为系统提供的类添加分类。还有就是将自己的类分模块,将实现不同功能的方法写在不同的分类中,一个类可以有无限个分类。

  1.分类又称为非正式协议:NSObject类及其子类的一个类别(catagory)。

#import "Teacher.h"

@interface Teacher (Log)
+(void)log;
@end

#import "Teacher+Log.h"

@implementation Teacher (Log)
+(void)log{  NSLog(@"分类");}
@end

  如上面所写的分类,我们为Teacher类添加了一个分类"Teacher+Log.h",分类中声明并实现了类方法+(void)log;

  2.类的延展:匿名分类。

  在Teacher.m文件中

#import "Teacher.h"

@interface Teacher ()  //这种写法就是匿名分类

@property (nonatomic,copy)NSString *nickName;  //匿名分类中可以添加属性,但是该属性默认为@private,只能在本类中使用

+(void)log;  //声明了一个类方法log

@end

@implementation Teacher

@synthesize name = _name;
//实现该类方法
+(void)log
{
    NSLog(@"匿名分类");
}

@end

  接着我们调用log方法,出现如下打印

  

  这里证明:分类(非正式协议)会重写本类及其类扩展(匿名分类)的方法。

  3.catagory中匿名分类允许添加属性并会自动生成成员变量和set、get方法;但是在非正式协议中,允许你用@property声明一个属性,不会为你提供set、get方法以及成员变量,如果你直接使用的话,造成崩溃。如下

  1> 我为Teacher + MyProperty.h分类头文件中增加了一个属性nickName;

#import "Teacher.h"

@interface Teacher (MyProperty)

@property (nonatomic,copy)NSString *nickName;

@end

  2>使用(缺少set、get方法导致崩溃)

Teacher *tec = [[Teacher alloc] init];
tec.nickName = @"nickNmae";
NSLog(@"%@",tec.nickName);

// 打印
2016-01-24 16:58:13.656 分类[2002:159549] -[Teacher setNickName:]: unrecognized selector sent to instance 0x100213e10
2016-01-24 16:58:13.658 分类[2002:159549] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException‘, reason: ‘-[Teacher setNickName:]: unrecognized selector sent to instance 0x100213e10‘
*** First throw call stack:……

  3>如何在非正式协议(分类)中添加属性【oc针对这一现象,提供了一个解决方案:关联对象(Associated Object)】,在分类的.m文件中写上如下代码

#import "Teacher+MyProperty.h"
#import <objc/runtime.h>

static void *strKey = &strKey;

@implementation Teacher (MyProperty)

-(void)setNickName:(NSString *)nickName
{
    objc_setAssociatedObject(self, &strKey, nickName, OBJC_ASSOCIATION_COPY);
}

-(NSString *)nickName
{
     return objc_getAssociatedObject(self, &strKey);
}

@end

六、协议protocol

  1.有非正式协议,自然也有正式协议,protocol便是,用于声明一大堆方法,等待实现,只要某个类遵守了某个协议,那么这个类就拥有了该协议的所有方法声明。(注意:协议只存在.h声明文件)其格式如下:

@protocol Teach <NSObject>
@required;
-(void)teach:(NSString *)text;  // 声明了一个teach的方法,并且为必须实现,默认是@optional;

@end

//新建了一个名为Teach.h的协议头文件

  2.类可以遵守协议(Teacher遵守teach协议)

    

  3.实现方法

-(void)teach:(NSString *)text
{
    NSLog(@"老师教书");
}

  4.一个类只能继承自一个父类(继承的单根性),但是一个类可以遵守多份协议.

七、关联对象(Associated Object)--扩展

  1.关联对象类似于成员变量,但是它是在运行时被添加的。(用于解决分类中添加属性的问题,上面已经介绍了原因,这里不做介绍)

  2.我们可以把关联对象想象成一个OC对象,这个对象通过key连接到一个类的实例变量上。

  3.由于使用的是C接口,所以key是一个void指针(const void *)。我们还需要指定一个内存管理策略,以告诉Runtime如何管理这个对象的内存。内存管理策略选项值如下:

// 当宿主对象被释放时,会根据指定的内存管理策略来处理关联对象。当我们需要在多个线程中处理访问关联对象的多线程代码时,这就非常有用了
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
    OBJC_ASSOCIATION_RETAIN = 01401,
    OBJC_ASSOCIATION_COPY = 01403
};
/* 如果指定的策略是assign,则宿主释放时,关联对象不会被释放;
    而如果指定的是retain或者是copy,则宿主释放时,关联对象会被释放。
    我们甚至可以选择是否是自动retain/copy。
*/

  4.使用

  1> void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)方法:将一个对象连接到其它对象

    参数1:源对象,一般传self.

    参数2:key,用来表示是哪一属性的key,可能在分类中添加不止一个属性。

    常见有三种写法:

    ① static void *strKey = &strKey;

    ② static NSString *strKey = @"strKey";

    ③ static char strKey;

    参数3:关联的对象

    参数4:关联策略

  self对象将获取一个新的关联的对象anObject,且内存管理策略是自动retain关联对象,当self对象释放时,会自动release关联对象。另外,如果我们使用同一个key来关联另外一个对象时,也会自动释放之前关联的对象,这种情况下,先前的关联对象会被妥善地处理掉,并且新的对象会使用它的内存。

  2> id anObject = objc_getAssociatedObject(self, &myKey)方法 ,获取关联的对象

  3> objc_removeAssociatedObjects(self)方法,移除所有关联

  5、应用(UIAlertView + Block分类,以自身为delegate监控选项按钮点击事件,以block作为关联对象)

#import <UIKit/UIKit.h>
typedef void(^CompleteBlock) (NSInteger buttonIndex);

@interface UIAlertView (Block)

// 用Block的方式回调,这时候会默认用self作为Delegate
- (void)showAlertViewWithCompleteBlock:(CompleteBlock) block;

@end

#import "UIAlertView+Block.h"
#import <objc/runtime.h>

@implementation UIAlertView (Block)

static char key;

// 用Block的方式回调,这时候会默认用self作为Delegate
- (void)showAlertViewWithCompleteBlock:(CompleteBlock)block
{
    if (block) {
        //移除所有关联
        objc_removeAssociatedObjects(self);
        /**
         1 创建关联(源对象,关键字,关联的对象和一个关联策略。)
         2 关键字是一个void类型的指针。每一个关联的关键字必须是唯一的。通常都是会采用静态变量来作为关键字。
         3 关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的;关联是原子的还是非原子的。这里的关联策略和声明属性时的很类似。
         */
        objc_setAssociatedObject(self, &key, block, OBJC_ASSOCIATION_COPY);
        //设置delegate
        self.delegate = self;
    }
    [self show];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    ///获取关联的对象,通过关键字。
    CompleteBlock block = objc_getAssociatedObject(self, &key);
    if (block) {
        ///block传值
        block(buttonIndex);
    }
}
/**
 OC中的关联就是在已有类的基础上添加对象参数。来扩展原有的类,需要引入#import <objc/runtime.h>头文件。关联是基于一个key来区分不同的关联。
 常用函数:  objc_setAssociatedObject     设置关联
 objc_getAssociatedObject     获取关联
 objc_removeAssociatedObjects 移除关联
 */
//当然也可以不必这么麻烦,使用继承同样可以轻易做到。
@end

八、类的加载(认识两个方法)

/**
 * 这两个方法是在程序运行一开始就被调用的方法.
 * 我们可以利用他们在类被使用前,做一些预处理工作.
 * 比如我碰到的就是让类自动将自身类名保存到一个NSDictionary中.
 */

//而initialize是在类或者其子类的第一个方法被调用前调用。
+(void)initialize;

//load方法是只要类所在文件被引用就会被调用.
+(void)load;

/**
 * 所以如果类没有被引用进项目,就不会有load调用;
 * 但即使类文件被引用进来,但是没有使用,那么initialize也不会被调用
 */

九、反射

  1.反射:简而言之就是通过类名返回类的对象(使用前导入#import <objc/runtime.h>头文件,今天前面的关联对象也用到这个头文件,好像关于这个runtime要学的东西有点多,复习着一下子扒出了好多要学的新东西,- _ -,先从基础的慢慢来吧)

// 类名返回类对象
+(NSObject *)createBean:(NSString *)className
{
    Class tempClass =  NSClassFromString(className);
    NSObject *obj;
    if (tempClass)
    {
        obj = [[tempClass alloc]init];
    }
    return obj;
}

  2.类名得到属性名集合

+(NSArray *)propertyOfClass:(NSString *)className
{
    NSMutableArray *arr = [NSMutableArray arrayWithCapacity:0];
    //通过类名获得类的属性
    const char *cClassName = [className UTF8String];

    id theClass = objc_getClass(cClassName);

    unsigned int outCount, i;

    objc_property_t *properties = class_copyPropertyList(theClass, &outCount);
    for (i = 0; i < outCount; i++)
    {
        objc_property_t property = properties[i];
        NSString *propertyNameString = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
        [arr addObject:propertyNameString];
    }
    return arr;
}

 3.通过反射(结合KVC),通过类名、数据源、约定,我们就可以实现json字符串转对象。

 4.具体之后三方库学习MJExtension中NSObject + MJClass转模型。

时间: 2024-10-05 02:54:30

【IOS学习基础】OC类的相关的相关文章

iOS学习笔记---oc语言第四天

字符串 数组 一.使用苹果帮助文档 学会使?用苹果帮助?文档是开发者的?一项技能 Inherits from 继承?自 Conforms to 遵循什么协议 Framework 属于哪个框架 Availability 什么时候可?用的 Declared in 声明在什么头文件?里 Related documents 相关文档 Sample code ?示例代码 快速打开帮助文档 在代码中,将?鼠标停留在 类名或者?法名上,option+?鼠标左键,点击 Reference的超链接进?入帮助?文档

【IOS学习基础】NSObject.h学习

一.<NSObject>协议和代理模式 1.在NSObject.h头文件中,我们可以看到 // NSObject类是默认遵守<NSObject>协议的 @interface NSObject <NSObject> { Class isa OBJC_ISA_AVAILABILITY; } // 往上翻看到NSObject协议的声明@protocol NSObject/* 中间一大堆方法的声明*/@end 然后我就产生疑问了,为什么我们自己定义的协议是这样,后面加上了<

iOS学习笔记---oc语言第三天

继承.初始化方法 一.继承 继承的上层:父类  继承的下层:子类 继承是单向的,不能相互继承 继承具有传递性:A继承于B,B继承于C,A具有B和C的特征和行为 子类能继承父类全部的特征和行为(私有变量也继承过来了,只是不能访问) 面向对象提供了继承语法.能大大简化代码,把公共的方法和实例对象写在父类里.子类只需要写自己独有的实例变量和方法即可 继承既能保证类的完整,又能简化代码 继承特点 oc中只允许单继承 没有父类的类称为根类,oc中得根类是NSObject(祖宗) 继承的内容:所有的实例变量

iOS学习笔记---oc语言第五天

字典,数组 ,集排序 一.字典类 存储以下数据 name:张三; sex:男;age:18 film:风暴; playcount:10000次;price:60元 字典类用于保存具有映射关系(key-value对)的数据 对于“name:张三”来讲,key就是“name”,key对应的value是“张 三” 一个key-value对认为是一个元素(实体),字典是存储key-value对 的容器. 特点: 与数组不同,数组靠下标存取数据,数组的下标是唯一的. 字典靠key存取元素.key不能重复,

iOS学习笔记---oc语言第二天

实例变量与方法 一.实例变量的可见度 二.方法 oc中的方法分两种:类方法和实例方法 类方法:只能类使用 eg:+ (id)alloc  注:类方法中不能使用实例变量 实例方法:只能对象使用,eg:- (void)sayHi iOS学习笔记---oc语言第二天

iOS学习笔记---oc语言第七天

类的扩展 NSDate是Cocoa中用于处理日期和时间的基础类,封装了某一给定的时刻,具体的日期 时间和时区 使用+date方法获取当前日期和时间 1 NSDate *date = [NSDate date];//获取当前时间 2 NSLog(@"%@",date);//无论你是哪个时区的时间,打印的总是o时区的时间 3 NSDate *date2 = [NSDate dateWithTimeIntervalSinceNow:60*60*8];//从现在开始之后的时间 4 NSLog(

iOS学习笔记---oc语言第六天

Block .数组高级 block本质上就是匿名函数(没有名称的函数) block语法和函数指针很相似 回顾函数 函数:C语?中,实现某一类功能的代码段. 完整的函数包含两部分:函数声明.函数定义 函数声明,即函数原型.例如:int sum(int x,int y);具有两个整型参 数,一个整型返回值的函数. 函数定义,即函数实现.例如:int sum(int x,int y){     retrun x + y;     } 回顾函数指针 函数指针(变量):存放函数地址(函数名)的指针变量.

IOS学习 - 方法选择器Selector的相关使用

问题的由来 Objective-C是一门动态的语言,只要有可能,Objective-C总会使用动态的方式来解决问题,比如它尽可能的将编译和连接要做的事推迟到运行时,所以它需要一个强大的运行时系统(runtime)来执行编译好的代码.runtime的角色类似于Objective-C语言的操作系统,是Objective-C的灵魂.Objective-C的很多特性都是基于这个强大的运行时的,是十分值得去学习理解的. 动态语言Objective-C很常见的一个消息发送语句(比较准确的说是消息发送,在不混

【IOS学习基础】内存管理

1.内存几大区域 1> 栈区:局部变量(基本数据类型.指针变量). 2> 堆区:程序运行的过程中动态分配的存储空间(创建的对象). 3> BSS段:没有初始化的全局变量和静态变量. 4> 数据区:已经初始化的全局变量和静态变量.(字符串常量) 5> 代码段:程序编译后的代码的内容. 2.引用计数器 1> 引用计数器:每个继承自NSObject的对象都有一个引用计数器,用来表示当前对象有几个拥有者. 2> 引用计数器的作用:用来判断对象是否应该回收. 3> 引