博客> OC 类、对象、内存管理
OC 类、对象、内存管理
2018-12-18 06:34 评论:0 阅读:1211 ZXY_Start
oc 内存管理 对象

OC中绝大多数的类都是继承自NSObject(NSProxy为一个特例),接下来我将从类中的实例变量、属性、选择器、类的构建流程、继承、类目等方面来进行讲解。

如下代码所示:Person.h

#import <Foundation>
@interface Person : NSObject
{
    NSString *name;
    NSInteger age;
    NSString *address;
    NSString *card_id;
}
-(NSString *)regist_School;
-(NSString *)description;
@end
第一行代码使用了import语句倒入了Foundation框架,使用import语句由如下优势:
1)import是对include的改进
2)import语句能够防止递归包含
如果导入的是系统的框架请使用<>,对其他文件的引用使用"",如:#import "Person.h"
@interface 表明这是一个接口,对应有实现@implementation,在编译期间,编译器会根据接口文件生成对应的数据结构(struct)。
Person 类中声明了四个实例变量(实例变量与传统意义的属性有所区别,如下会提到)和两个方法。那么声明了实例变量和方法,如何实例化和实现呢?OC的类通常是由头文件(接口文件)和实现文件构成的(一般的做法是将头文件与实现文件分开,利于整体的组织结构,如果只实现了接口文件,没有实现文件会发生什么呢?)。
Person类的实现文件如下:
#import "Person.h"
@implementation Person
- (id)init{
    if(self = [super init]){
        name = @"张三";
        age  = 15;
        address = @"长沙";
        card_id = @"116600A";
    }
    return self;
}

- (NSString *)regist_School{
    return @"Ok";
}

- (NSString *)description{
    return [NSString stringWithFormat:@"姓名:%@",name];
}
@end
实现文件首先引入了该类的接口文件,告诉编译器我是它(接口文件)的实现,runtime有需要请来找我。
init方法为NSObject的初始化方法,由子类继承,我们可以对此方法进行扩展,但是都需要调用父类的init方法[super init]即返回一个Person的对象,每一个类的实例对象都有一个隐藏的参数self代表该实例自身,就像生活中的我表示第一人称,self = [super init]表示你反回的对象给我了,由于某些原因,self可能为nil(这种情况常有发生,例如NSURL指定的URL存在错误就会得到nil的对象),所以需要if来判断self是否为nil,如果self不等于nil,就需要对实例变量初始化。
Person *p = [[Person alloc]init];
alloc方法会调用+(instancetype)allocWithZone:(struct _NSZone *)zone,并传递NULL参数,该方法会为类的实例化分配一块内存并清零,用来存储实例变量、方法等内容。init方法用来初始化对象,alloc用来为对象中的实例变量、属性、方法等开辟内存空间,而init需要为对象开辟内存,如对象p前的*标志着p指向该内存。

属性

刚才提到过实例变量与属性不同,那么区别在哪里?
属性是使用@property声明的变量,早期的OC编译器没有引入垃圾回收机制(ARC,称为自动引用计数,这是一种垃圾回收的算法,java使用的垃圾回收机制叫做gc,算法类似标记-清除法),在使用实例变量的时候不得不考虑内存的问题例如set方法需要这么写:
- (void)setName:(NSString *)pro_name_n
{
    [pro_name_n retain];
    [name release];
    name = pro_name_n;
}
可以看到,如果存在多个对外开放的实例变量的话,set和get方法会占据大量的篇幅,代码会很混乱,之后苹果引进了@property,在接口使用可以自动生成上边的set代码:@property(nonatomic , retain)NSString *pro_name;在实现文件中可以使用@synthesize pro_name = name;生成get方法。为了更加方便,干脆在使用@property时如果没有定义实例变量,编译器会为你自动生成实例变量如:@property(nonatomic , retain)NSArray *cl_arr; 相当于定义了cl_arr属性和_cl_arr实例变量,同时也不需要使用@synthesize为该变量生成get方法,系统会在你使用了@property时帮你。
如果你使用@property却又想自己实现get和set方法的话,那就用@dynamic标记上@dynamic pro_age;

方法选择器SEL

选择器,就是我们常说的方法,有方法名和实现、参数、返回值。Object-C是动态的调用方法,在运行中,存在一张选择器的表对应选择器的名字和指针,当调用方法时会查找该表并将参数传递。OC方法调用实质为消息的分发机制:void objc_msgSend(void /* id self, SEL op, ... */ ),在调用OC方法时,runtime会将消息和receiver通过msgSend发送,其中第一个参数为消息的接受对象,第二个参数为选择器,之后伴随着传入方法的参数。

对象

什么是对象,什么是类?
本人的理解,类代表具有某种共性事物的总称,是一种集合、概念,例如某一类人,这类人具有先天的运动天赋,这是类,而对象呢?姚明就是一个对象,是共性独特的个体。再举一个比较简单的栗子:车是类,这个类应该具有四个轮子、发动机、方向盘等,而宝马X5就是这个类的对象。
那么我们可以说,对象是对一个类的实现,是具有类多种共性的一个个体,表现出类的共性和自身的个性。对象需要而且通常必要具有自身的个性,如车的颜色、车轮的大小。
OC 的对象:Person *p = [[Person alloc]init];这个人就已经实例化了,已经是一个完整的对象了,你可以让他介绍自己,也可以给他取名字。

对象实例化过程

对象的实例化过程实在程序运行时完成的,每一个类都存在一个指针isa代表此类对象的指针,同时类对象也包括一个指向父类的指针(所以也就清楚了为何子类查找不到的方法会向上级寻找)、实例变量列表、选择器列表、协议列表:
struct objc_class {
Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
实例化时,会对类的实例变量、方法、协议等列表,需要调用的时候移动指针即可。
每一个类都可以使用Class表示:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

内存管理

由于苹果公司推出了ARC(引用计数)机制,大部分情况下我们不需要考虑内存问题。
1)如果对于大量数据的遍历、操作等如下代码:
    for(int i = 0 ; i &lt; 1000000; i++){
        NSArray *arr = [[NSArray alloc]init];
        [arr count];
    }
这种情况下,如果不及时释放掉arr的话,会由于大量未来得及释放的内存而使得应用程序垮掉。解决方法
    加上autorelease
    for(int i = 0 ; i &lt; 1000000 ; i ++){
            @autoreleasepool {
                NSArray *arr = [[NSArray alloc] initWithObjects:@[[NSNumber numberWithInteger:i]], nil];
                NSLog(@"arr is now %d",arr[0]);
            }
        }
2)循环引用
    通常需要对delegate 设置属性weak,保证对象能够被销毁。
3)CF系列的对象所有权
    由于ARC只能够收回OC的对象(其实ARC的机制只是编译的时候在你的代码中自动生成retain和release,从这点来看,C、C++等对象的内存,大部分情况下应该由开发者管理),在面对CoreFoundation框架时将会面临对象所有权的问题,这个对象归谁管,使用ARC的前提条件是必须是可保留的对象指针,在面对CF对象的时候,由于CF框架中的对象并不是可保留对象指针所以需要通过桥接转换,告诉编译器哪个对象为指针的拥有者,桥接有三种方式:__bridge、__bridge_retained和__bridge_transfer。
   NSString *OC_s = @"Test";
   CFStringRef cf_string = (__bridge_? CFStringRef)OC_s;
    1)__bridge只会传递指针,不管所有权,所有权依然有OC_s管理
    2)__bridge_retained所有权移交,cf_string负责release(CFRelease),而且,引用计数+1
    3)__bridge_transfer由CF将所有权转交到OC中,由OC来释放(ARC可自动管理)
收藏
2
sina weixin mail 回到顶部