一些基本知识

1.面向过程与面向对象

OC中的类是面向对象,C语言中的结构体是面向过程。OC不能直接编译,需要由runtime转换成纯C在进行编译。
例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用分别的函数来实现,问题就解决了。
而面向对象的设计思路是:可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
由此可见,面向过程是以步骤来划分问题的,而面向对象是以功能来划分问题的。

2.预处理指令

预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。预处理过程先于编译器对源代码进行处理。
预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行响应的转换。预处理过程还会删除程序中的注释和多余的空白字符。
预处理指令是以#号开头的代码行。常见的预处理指令如下:

  • #空指令,无任何效果
  • #include 包含一个源代码文件
    重复使用include会因为重复定义报错,但是重复使用import可以
    会在b.h中报出重复定义错误。如下:
#include "b.h"
#include "b.h"

可以使用:

#import "b.h"
#import "b.h"

在较为复杂的情况下,例如类A和类B均include头文件C,那么在类D同时include类A和类B时也会出现上述错误,全部使用import则不会存在问题。
另一种解决办法就是:

#ifndef _C_H
#define _C_H
#include "c.h"
#endif

用import就不存在该问题,所以不必使用ifndef和define强调。当创建.h文件和.pch文件时系统会自动生成带有ifndef和define这对预处理命令组合,所以它们应该很少会被手动用到。

  • #define 定义宏
#define SizeValueBase6Plus(value) ((value) / 414.0f * [UIScreen mainScreen].bounds.size.width)
#define FONTSIZE(size) [UIFont systemFontOfSize:SizeValueBase6Plus(size)]
  • #undef 取消已定义的宏
  • #if 如果给定条件为真,则编译下面代码
    用来区分生产联调等各环境基本配置信息。更改宏定义的值即可
#define IS_RELEEASE 1
#if IS_RELEEASE
NSString *const APP_KEY = @"67585b150d92de590a5767c624533b88";
#else
NSString *const APP_KEY = @"366490c0efde1128c99fc634c51bca5f";
#endif
  • #ifdef 如果宏已经定义,则编译下面代码
    通过判断前面是否有#define DEBUG来判断是否打印,可在代码手动控制打开注销。
#ifdef DEBUG
print ("device_open(%p)\n", file);
#endif

在.pch文件中强调非OC语言不能调用。 防止代码中有.cpp,.mm文件时会报错。

#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif
  • #ifndef 如果宏没有定义,则编译下面代码
    如果没有宏定义过该文件,则宏定义它,并执行以下代码。如果宏定义过该文件,则什么也不做。以此来保证内部代码只编译一次。在.h文件和.pch文件创建事系统会自动生成,保证内部引用和宏定义不会重复编译。
#ifndef HWPrefixHeader_pch
#define HWPrefixHeader_pch
#endif /* HWPrefixHeader_pch */
  • #elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
  • #endif 结束一个#if……#else条件编译块
  • #error 停止编译并显示错误信息
  • #pragma mark - 代码中段落方法注释

3.typedef和struct的使用

typedef主要用于把已知类型更换成自定义的名字。struct结构体在OC中很少使用,OC中主要采用对象。
typedef和struct的使用方式:

struct Date1{
    int year;
    int month;
    int day;
};
- (void)example1{
    struct Date1 *birthday = NULL;
    birthday->day = 22;
    birthday->month = 10;
    NSLog(@"%d月%d日",birthday->month,birthday->day);
}

struct Date2{
    int year;
    int month;
    int day;
};
typedef struct Date2 MyDate2;
- (void)example2{
    MyDate2 *birthday = NULL;
    birthday->month = 10;
    birthday->day = 20;
    NSLog(@"%d月%d日",birthday->month,birthday->day);
}


struct Date3{
    int year;
    int month;
    int day;
};
typedef struct Date2 *MyDate3;
- (void)example3{
    MyDate3 birthday = NULL;
    birthday->month = 10;
    birthday->day = 20;
    NSLog(@"%d月%d日",birthday->month,birthday->day);
}

typedef和struct在系统中的应用,如在objc.h中:

typedef struct objc_class *Class;
struct objc_class : objc_object {
    Class superclass;
    const char *name;
    uint32_t version;
    uint32_t info;
    uint32_t instance_size;
    struct old_ivar_list *ivars;
    struct old_method_list **methodLists;
    Cache cache;
    ......
};

Class实际上是objc_class结构体的指针。
使用typedef定义block。如下:

typedef void(^block8)(int,int);
 [self block8with:^(int a, int b) {
        NSLog(@"结果为%d",a+b);
    }];
- (void)block8with:(block8)block
{
    block(5,5);
}

直接使用block8代替void(^)(int,int)。
使用typedef在C语言中定义函数指针。常见于OC底层代码实现。使用方法如下:

typedef int (*calFun)(int,int);
int add(int a,int b)
{
    return a+b;
}

int sub(int a,int b)
{
    return a-b;
}

void testfun(int type)
{
    calFun fun;
    if (type == 1) {
        fun = add;
    }else
    {
        fun = sub;
    }
    int result = (*fun)(10,5);
    NSLog(@"结果为%d",result);
}

定义一个名为calfun,返回值为int参数为两个int的函数类型。

3.@synthesize

声明一个属性

@property(nonatomic,retain) NSString    *name; 

“属性”(property)有两大概念:ivar(实例变量)、存取方法(access method=getter),即@property = ivar + getter + setter。

默认情况下,编译器会为当前类自动生成一个NSString *_name的实例变量,同时提供访问该实例变量的getter和setter方法(简化工作量)。即LLVM会自动生成@synthesize name = _name,也代表属性有name和_name两个变量名。同时可以使用@synthesize关键字改变实例变量的别名。而实际上,我们使用_name操作实例变量,使用self.name操作的是方法(等号左是set方法,等号右边是get方法),而非属性。

当setter和getter方法同时重写时,需要手动添加@synthesize关键字。
@dynamic关键字会不自动生成getter和setter方法。

4. 修饰词

atomic/nonatomic

指定合成存取方法是否为原子操作,可以理解为是否线程安全,但在iOS上即时使用atomic也不一定是线程安全的,要保证线程安全需要使用锁机制,超过本文的讲解范围,可以自行查阅。
可以发现几乎所有代码的属性设置都会使用nonatomic,这样能够提高访问性能,在iOS中使用锁机制的开销较大,会损耗性能。

readwrite/readonly

readwrite是编译器的默认选项,表示自动生成getter和setter,如果需要getter和setter不写即可。
readonly表示只合成getter而不合成setter。

assign

assign表示对属性只进行简单的赋值操作,不更改所赋的新值的引用计数,也不改变旧值的引用计数,常用于标量类型,如NSInteger,NSUInteger,CGFloat,NSTimeInterval等。
assign也可以修饰对象如NSString等类型对象,上面说过使用assign修饰不会更改所赋的新值的引用计数,也不改变旧值的引用计数,如果当所赋的新值引用计数为0对象被销毁时属性并不知道,编译器不会将该属性置为nil,指针仍旧指向之前被销毁的内存,这时访问该属性会产生野指针错误并崩溃,因此使用assign修饰的类型一定要为标量类型。

unsafe_unretained

使用unsafe_unretained修饰时效果与assign相同,不会增加引用计数,当所赋的值被销毁时不会被置为nil可能会发生野指针错误。unsafe_unretained与assign的区别在于,unsafe_unretained只能修饰对象,不能修饰标量类型,而assign两者均可修饰。

strong

strong表示属性对所赋的值持有强引用表示一种“拥有关系”(owning relationship),会先保留新值即增加新值的引用计数,然后再释放旧值即减少旧值的引用计数。只能修饰对象。如果对一些对象需要保持强引用则使用strong。

weak

weak表示对所赋的值对象持有弱引用表示一种“非拥有关系”(nonowning relationship),对新值不会增加引用计数,也不会减少旧值的引用计数。所赋的值在引用计数为0被销毁后,weak修饰的属性会被自动置为nil能够有效防止野指针错误。
weak常用在修饰delegate等防止循环引用的场景。

copy

copy修饰的属性会在内存里拷贝一份对象,两个指针指向不同的内存地址。
一般用来修饰有对应可变类型子类的对象。
如:NSString/NSMutableString,NSArray/NSMutableArray,NSDictionary/NSMutableDictionary等。
为确保这些不可变对象因为可变子类对象影响,需要copy一份备份,如果不使用copy修饰,使用strong或assign等修饰则会因为多态导致属性值被修改。

_block是用来修饰一个变量,这个变量就可以在block中被修改
_weak是用来修饰一个变量,这个变量不会在block代码块中被retain,用来避免循环引用。

5.堆和栈与数据类型

堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

OC对象需要进行内存管理,而其它非对象类型比如基本数据类型就不需要进行内存管理

Objective-C的对象在内存中是以堆的方式分配空间的。堆里面的内存是动态分配的,所以也就需要程序员手动的去添加内存、回收内存。
非OC对象一般放在栈里面(栈内存会被系统自动回收)

举例说明:

int main(int argc, char *argv[])
{
    @autoreleasepool {
        int a = 10;
        int b = 20;
        Car *c = [[Car alloc] init];
    }
}
栈与堆

当代码块一过,里面的a,b,*c指针都会被系统编译器自动回收,因为它存放在栈里面,而OC对象则不会被系统回收,因为它存放堆里面,堆里面的内存是动态存储的,所以需要程序员手动回收内存。

在C中,当使用Studet stu;创建对象时,创建的对象在栈中。使用Student *pStudent = new Student创建时,对象在堆中,需要手动释放。

在堆上创建对象是匿名的,必须要有一个指针指向它,否则无法使用。而在栈上创建对象,都用名字,不必须用指针指向它,由系统管理内存。

6.基本类型与OC对象

int,bool等初始化不需要*的为基本类型即值类型,存在于栈,不需要管理内存,赋值与作为参数传递会进行拷贝,拷贝后的值与原值内容相同地址不同,修改其中一个不会影响另一个。

而array,dictionary等初始化需要*的为对象类型即引用类型,存在于堆,需要管理内存,赋值与作为参数传递不会进行拷贝而是会对引用计数进行加1处理,实际上地址相同,改变其中一个会影响另一个。

string是比较特殊的对象类型。

7.NSString与NSMutableString

NSString不同之处在于使用常量字符串去初始化一个NSString,而系统会对常量字符串进行优化,所有引用同一个常量字符串的NSString共享同一块内存,这块内存位于常量区,引用计数为7fffffff,表示不使用通常的引用计数管理机制,不会释放,所以会处于栈而不是堆.

NSString 的对象就是stack(栈) 中的对象,NSMutableString 的对象就是heap(堆) 中的对象。前者创建时分配的内存长度固定且不可修改;后者是分配内存长度是可变的,可有多个owner, 适用于计数管理内存管理模式。

两类对象的创建方法也不同,前者直接创建“NSString * str1=@"welcome"; “,而后者需要先分配再初始化“ NSMutableString * mstr1=[[NSMutableString alloc] initWithString:@"welcome"]; ”。
NSString对应的赋值与作为参数传递等所有操作都是进行拷贝并使用所返回新的string(值类型),而NSMutableString是根据地址是真正意义上的在原字符串上操作(对象类型)。


8.分类

例a.h代码如下:

@interface a : NSObject

@end

//这是定义的一个extension(扩展),在扩展中是可以添加属性的
//并且注意,扩展中添加的属性是private的,对外是不可访问的(当然是运行时和kvc除外)
//extension实际上是一个“匿名”的category
//extension可以定义在.h或者.m文件中都可以
@interface a()

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *sex;

@end

//这里又定义一个extension
//一个class可以定义多个extension
//实际上运行时的时候extension和原文件没有区别
@interface a()

@property (strong, nonatomic) NSString *age;

@end

例a.m代码如下:

#import "a.h"
#import <UIKit/UIKit.h>

//这里定义了一个category,注意在category中可以为任何文件扩展函数,但是不能添加属性(使用运行是才可以)
//扩展里面定义的函数是public的,所有继承者都拥有定义的功能
//扩展的定义可以在.h或者.m中
//扩展在官方的各种框架中是很常用的,和swift中的extension类似
@interface a(handlerAccount)<UITableViewDelegate>

//定义一个接口
- (a*)getAccount;

@end

//这里是上面定义的扩展的实现
@implementation a (handlerAccount)

- (a *)getAccount
{
   a *test = [[a alloc] init];
   test.name = @"mark";
   test.sex = @"man";
   test.age = @"12";
   return test;
}

@end

//这里直接定义一个category的实现
//可以用来分离代码
@implementation a (tableViewDelegate)

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    
}

@end

分类也可以用来将一个复杂的类中的代码分块(swift的extension可以有相似的作用), 使得代码组织更好, 例如可以将tableView的delegate, 和Datasource在分类中实现。
但是在使用category来扩展Cocoa的原生类的时候, 要注意函数的命名如果是和原生已有的函数名相同,那么将会发生不可预料的结果(不能确定哪一个方法在运行时会被调用), 因此建议在自己的函数名前面加上前缀)。

9.继承

继承一个类可以拥有基类的方法的接口和实现
OC中是不支持多继承的,因为多继承的设计模式效率低下,那么如何在OC中实现多继承

  • 组合方式
    初始化想要继承类的实例,并使用该实例调用即可
  • 协议方式
    定义想要继承类方法的协议,并遵循该协议。不过这样只能继承该类的方法接口,不会继承方法的具体实现。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 重点掌握 3 类对象和方法 对象就是一个物体 类的独特存在就是一个实例,对实例进行操作叫做方法。方法可以应用于类或...
    Coder大雄阅读 1,254评论 0 2
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,112评论 29 470
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 1,949评论 0 7
  • 开始前的思考 既然从零开始,那么在创建项目目录结构的时候我考虑到的问题有: 1、项目中有哪些功能?2、各功能如何实...
    德山_阅读 698评论 0 1
  • 你有女朋友吧 你们还一起去了京都 聪明的我分分钟就发现了 不知道 但是我知道这不是我想要的。 嗯。 用爱换一生灿烂...
    学霸小朋友阅读 148评论 0 0