OC总结

//

//  main.m

//  OC总结

//

//  Created by mac on 16/4/27.

//  Copyright (c) 2016 mac. All rights reserved.

//


#import <Foundation/Foundation.h>


int main(int argc, const char * argv[]) {

    @autoreleasepool {

        /*

         题目:程序运行的时候提示下列信息

         请输入相应数字选择需要执行的运算:

         1 加法

         2 减法

         

         用户选择运算后,再提示用户输入两个需要进行运算的整数,输入完毕后就输出运算结果

         */

        

        printf("请输入相应数字选择需要执行的运算:\n");

        printf("1 加法\n");

        printf("2 减法\n");

        

        int a = 0;

        while (a != 1 & a != 2) {

            NSLog(@"请输入12");

            scanf("%d", &a);

        }

        

        int n1 = 0.5;

        int n2 = 0.5;

        

        NSLog(@"请输入第一个整数:");

        scanf("%d", &n1);

        NSLog(@"请输入第二个整数:");

        scanf("%d", &n2);


        if (a == 1){

             NSLog(@"两个数的和是%d", n1 + n2);

        }else{

            NSLog(@"两个数的差是%d", n1 - n2);

        }

        

        

    }

    return 0;

}

/*

 

1.

 

#import <Foundation/NSObjCRuntime.h>

NSObjCRuntime.h中有NSLog函数的声明


 

 

2.

 

Foundation框架头文件的路径

1> 右击Xcode.app --> 显示包内容

2> Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/Foundation.framework


 

 

 

3.

 

 // [Car new]每次都会创建出一个新对象,并且会返回新对象本身(新对象的地址)

 Car *p = [Car new];

// OC是在运行过程中才会检测对象有没有实现相应的方法

 类方法的好处和使用场合

 1> 不依赖于对象,执行效率高

 2> 能用类方法,尽量用类方法

 3> 场合:当方法内部不需要使用到成员变量时,就可以改为类方法

 

 

 

4.

 

super使用场合:子类重写父类的方法时想保留父类的一些行为

// 僵尸

@interface Zoombie : NSObject

- (void)walk;

@end


@implementation Zoombie

- (void)walk

{

    NSLog(@"往前挪两步******");

}

@end


// 跳跃僵尸

@interface JumpZoombie : Zoombie


@end


@implementation JumpZoombie

- (void)walk

{

    // 跳两下

    NSLog(@"跳两下");

    

    // 走两下(直接调用父类的walk方法)

    [super walk];

    //NSLog(@"往前挪两步----");

}

@end

 

 

 

 

 

 

 5.多态

 

 1>.没有继承就没有多态

 2>.代码的体现:父类类型的指针指向子类对象

 3>.好处:如果函数\方法参数中使用的是父类类型,可以传入父类、子类对象

 void feed(Animal *a)

 {

    NSLog(@"喂动物----");

 }

 Animal *aa = [Animal new];

 feed(aa);

 Dog *dd = [Dog new];

 feed(dd);

 Cat *cc = [Cat new];

 feed(cc);

 4>.局限性:

 父类类型的变量 不能 直接调用子类特有的方法。必须强转为子类类型变量后,才能直接调用子类特有的方法

 [aa run];

 aa转为Dog *类型的变量

 Dog *dd = (Dog *)aa;

 [dd run];


 

 

 

 6.

 #pragma mark

 #pragma mark -

 

 

 

 

 

 

 7.成员变量的作用域

 

 @public : 在任何地方都能直接访问对象的成员变量

 @private : 只能在当前类的对象方法中直接访问(@implementation中默认是@private

 @protected : 可以在当前类及其子类的对象方法中直接访问  @interface中默认就是@protected

 @package : 只要处在同一个框架中,就能直接访问对象的成员变量

 

 @interface@implementation中不能声明同名的成员变量


 

 

 

 

 

 8.

 

 id  == NSObject *

 

 

 

 

 

 

 9.构造方法

 

 完整地创建一个可用的对象

 1.分配存储空间  +alloc

 2.初始化 -init

// 1.调用+alloc分配存储空间

// Person *p1 = [Person alloc];

// 2.调用-init进行初始化

// Person *p2 = [p1 init];


 构造方法:用来初始化对象的方法,是个对象方法,-开头

 重写构造方法的目的:为了让对象创建出来,成员变量就会有一些固定的值

 重写构造方法的注意点

 1.先调用父类的构造方法([super init]

 2.再进行子类内部成员变量的初始化

  

 重写-init方法

 - (id)init

 {

      1.一定要调用回superinit方法:初始化父类中声明的一些成员变量和其他属性

     self = [super init];  当前对象 self

 

 

      2.如果对象初始化成功,才有必要进行接下来的初始化

     if (self != nil)

     {  初始化成功

         _age = 10;

     }

    if ( self = [super init] )

    {

        _age = 10;

    }

 

     // 3.返回一个已经初始化完毕的对象

     return self;

 }

 

 自定义构造方法的规范

 1.一定是对象方法,一定以 - 开头

 2.返回值一般是id类型

 3.方法名一般以initWith开头

 

 - (id)initWithAge:(int)age

 {

    if ( self = [super init] )

    {

        _age = age;

    }

    return self;

 }

 

 

 

 10.分类

 

 分类的作用:在不改变原来类内容的基础上,可以为类增加一些方法

 使用注意:

 1.分类只能增加方法,不能增加成员变量

 2.分类方法实现中可以访问原来类中声明的成员变量

 3.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用

 4.方法调用的优先级:分类(最后参与编译的分类优先) --> 原来类  --> 父类

 

 

 

 

 

 

 11.类的本质

 

 1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法。只会调用一次。

 2.当第一次使用某个类时,就会调用当前类的+initialize方法 ( [[Person alloc] init]

 3.先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load方法)

 先初始化父类,再初始化子类(先调用父类的+initialize方法,再调用子类的+initialize方法)

 

 + (void)load

 {

 NSLog(@"Person---load");

 }

 

 // 当第一次使用这个类的时候,就会调用一次+initialize方法

 + (void)initialize

 {

 NSLog(@"Person-initialize");

 }

 

 

 

 

 

 12.description方法

 

 #import <Foundation/Foundation.h>

 #import "Person.h"

 

 void test9()

 {

 // 输出当前函数名

 NSLog(@"%s\n", __func__);

 }

 

 int main()

 {

 // 输出行号

 NSLog(@"%d", __LINE__);

 

 // NSLog输出C语言字符串的时候,不能有中文

 // NSLog(@"%s", __FILE__);

 

 // 输出源文件的名称

 printf("%s\n", __FILE__);

 

 test9();

 

 Person *p = [[Person alloc] init];

 

 // 指针变量的地址

 NSLog(@"%p", &p);

 // 对象的地址

 NSLog(@"%p", p);

 // <类名:对象地址>

 NSLog(@"%@", p);

 

 return 0;

 }

 

 void test2()

 {

 Class c = [Person class];

 

 // 1.会调用类的+description方法

 // 2.拿到+description方法的返回值(NSString *)显示到屏幕上

 NSLog(@"%@", c);

 }

 

 void test1()

 {

 Person *p = [[Person alloc] init];

 p.age = 20;

 p.name = @"Jack";

 // 默认情况下,利用NSLog%@输出对象时,结果是:<类名:内存地址>

 

 // 1.会调用对象p-description方法

 // 2.拿到-description方法的返回值(NSString *)显示到屏幕上

 // 3.-description方法默认返回的是类名+内存地址

 NSLog(@"%@", p);

 

 //Person *p2 = [[Person alloc] init];

 //NSLog(@"%@", p2);

 

 //NSString *name = @"Rose";

 

 //NSLog(@"我的名字是%@", name);

 

 Person *p2 = [[Person alloc] init];

 p2.age = 25;

 p2.name = @"Jake";

 NSLog(@"%@", p2);

 

 #import "Person.h"

 

 @implementation Person

 

 // 决定了实例对象的输出结果

 //- (NSString *)description

 //{

 //    // 下面代码会引发死循环

 //    // NSLog(@"%@", self);

 //    return [NSString stringWithFormat:@"age=%d, name=%@", _age, _name];

 //    //return @"3424324";

 //}

 

 // 决定了类对象的输出结果

 + (NSString *)description

 {

    return @"Abc";

 }

 @end

 

 

 

 

 

 

 13. SEL

 

 SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址。找到方法地址就可以调用方法

 其实消息就是SEL

 

 @selector:(test);

 NSSelctorFromString:(@"test");

 NSStringFromSelector:(@selector:(test));

 [p performSelector:@selector(test)];

 

#import <Foundation/Foundation.h>

#import "Person.h"


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;

}

 

 #import "Person.h"

 

 @implementation Person

 + (void)test

 {

    NSLog(@"test-----");

 }

 

 - (void)test2

 {

     // _cmd代表着当前方法

     

     NSString *str = NSStringFromSelector(_cmd);

     

     // 会引发死循环

     // [self performSelector:_cmd];

     

        NSLog(@"调用了test2方法-----%@", str);

 }

 

 - (void)test3:(NSString *)abc

 {

    NSLog(@"test3-----%@", abc);

 }

 @end

 

 

 

 

 

 

 14. 引用计数器

 

 1.方法的基本使用

 1> retain :计数器+1,会返回对象本身

 2> release :计数器-1,没有返回值

 3> retainCount :获取当前的计数器

 4> dealloc

 * 当一个对象要被回收的时候,就会调用

 * 一定要调用[super dealloc],这句调用要放在最后面

 

 2.概念

 1> 僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用

 2> 野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS

 3> 空指针 :没有指向任何东西的指针(存储的东西是nilNULL0),给空指针发送消息不会报错

 

 1.你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)

 2.你不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release

 3.retain,谁release

 4.alloc,谁release

 

 

 

 

 

 

 

 15.

 

 set方法的内存管理代码规范:

 1.只要调用了alloc,必须有releaseautorelease

 对象不是通过alloc产生的,就不需要release

 

 2.set方法的代码规范

 1> 基本数据类型:直接赋值

 - (void)setAge:(int)age

 {

 _age = age;

 }

 

 2> OC对象类型

 先判断是不是新传进来对象与当前对象是否为同一对象,如果不是同一对象,就release旧值,retain新值,并赋值给当前对象

 

 - (void)setCar:(Car *)car

 {

 // 1.先判断是不是新传进来对象

     if ( car != _car )

     {

         // 2.对旧对象做一次release

         [_car release];

 

         // 3.对新对象做一次retain

         _car = [car retain];

     }

 }

 

 3.dealloc方法的代码规范

 1> 一定要[super dealloc],而且放到最后面

 2> self(当前)所拥有的其他对象做一次release

 - (void)dealloc

 {

     [_car release];

     [super dealloc];

 }

 

 

 

 

 

 

 16. @property参数

 

 // retain : 生成的set方法里面,release旧值,retain新值

 @property (retain) Book *book;

 

 1.set方法内存管理相关的参数

 * retain : release旧值,retain新值(适用于OC对象类型)

 * assign : 直接赋值(默认,适用于非OC对象类型)

 * copy   : release旧值,copy新值

 

 2.是否要生成set方法

 * readwrite : 同时生成settergetter的声明、实现(默认)

 * readonly  : 只会生成getter的声明、实现

 

 3.多线程管理

 * nonatomic : 性能高 (一般就用这个)

 * atomic    : 性能低(默认)

 

 4.settergetter方法的名称

 * setter : 决定了set方法的名称,一定要有个冒号 :

 * getter : 决定了get方法的名称(一般用在BOOL类型)

 

 

 

 

 17. 循环引用

 

 1.@class的作用:仅仅告诉编译器,某个名称是一个类

 @class Person; // 仅仅告诉编译器,Person是一个类

 

 2.开发中引用一个类的规范

 1> .h文件中用@class来声明类

 2> .m文件中用#import来包含类的所有东西

 

 3.两端循环引用解决方案

 1> 一端用retain

 2> 一端用assign

 

 4.arc环境下,两端循环引用的解决方案

 1> 一端用strong

 2> 一端用weak

 

 

 

 

 18. autorelease

 

 1.autorelease的基本用法

 1> 会将对象放到一个自动释放池中

 2> 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作

 3> 会返回对象本身

 4> 调用完autorelease方法后,对象的计数器不变

 

 2.autorelease的好处

 1> 不用再关心对象释放的时间

 2> 不用再关心什么时候调用release

 

 3.autorelease的使用注意

 1> 占用内存较大的对象不要随便使用autorelease

 2> 占用内存较小的对象使用autorelease,没有太大影响

 

 自动释放池的创建方式

 1> iOS 5.0

 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

 

 [pool release]; // [pool drain];

 

 2> iOS 5.0 开始

 @autoreleasepool

 {

 

 }

 

 1.系统自带的方法里面没有包含allocnewcopy,说明返回的对象都是autorelease

 

 2.开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象

 1> 创建对象时不要直接用类名,一般用self

 + (id)person

 {

 return [[[self alloc] init] autorelease];

 }

 

 

 

 

 

 19. ARC的基本使用

 

 ARC的判断准则:只要没有强指针指向对象,就会释放对象

 1.ARC特点

 1> 不允许调用releaseretainretainCount

 2> 允许重写dealloc,但是不允许调用[super dealloc]

 3> @property的参数

 * strong :成员变量是强指针(适用于OC对象类型)

 * weak :成员变量是弱指针(适用于OC对象类型)

 * assign : 适用于非OC对象类型

 4> 以前的retain改为用strong

 

 指针分2种:

 1> 强指针:默认情况下,所有的指针都是强指针 __strong

 2> 弱指针:__weak


 

 

 

 

 20. block

 

 1> 如何定义block变量

 int (^sumBlock)(int, int);

 void (^myBlock)();

 

 2> 如何利用block封装代码

 ^(int a, int b) {

 return a - b;

 };

 

 ^() {

 NSLog(@"----------");

 };

 

 ^ {

 NSLog(@"----------");

 };

 

 3> block访问外面变量

 * block内部可以访问外面的变量

 * 默认情况下,block内部不能修改外面的局部变量

 * 给局部变量加上__block关键字,这个局部变量就可以在block内部修改

 

 4> 利用typedef定义block类型

 typedef int (^MyBlock)(int, int);

 // 以后就可以利用MyBlock这种类型来定义block变量

 MyBlock block;

 MyBlock b1, b2;

 

 b1 = ^(int a, int b) {

    return a - b;

 };

 

 MyBlock b3 = ^(int a, int b) {

    return a - b;

 };

 

 void test3()

 {

     int a = 10;

     __block int b = 20;

     

     void (^block)();

     

     block = ^{

     // block内部可以访问外面的变量

     //NSLog(@"a = %d", a);

     

     // 默认情况下,block内部不能修改外面的局部变量

     // a = 20;

     

     // 给局部变量加上__block关键字,这个局部变量就可以在block内部修改

     b = 25;

 };

 

 

 

 

 

 21. 协议

 

 1.协议的定义

 @protocol 协议名称 <NSObject>

 // 方法声明列表....

 @end

 

 

 2.如何遵守协议

 1> 类遵守协议

 @interface 类名 : 父类名 <协议名称1, 协议名称2>

 

 @end

 

 2> 协议遵守协议

 @protocol 协议名称 <其他协议名称1, 其他协议名称2>

 

 @end

 

 3.协议中方法声明的关键字

 1> @required (默认)

 要求实现,如果没有实现,会发出警告

 

 2> @optional

 不要求实现,怎样不会有警告

 

 4.定义一个变量的时候,限制这个变量保存的对象遵守某个协议

 类名<协议名称> *变量名;

 id<协议名称> 变量名;

 NSObject<MyProtocol> *obj;

 id<MyProtocol> obj2;

 

 如果没有遵守对应的协议,编译器会警告

 

 5.@property中声明的属性也可用做一个遵守协议的限制

 @property (nonatomic, strong) 类名<协议名称> *属性名;

 @property (nonatomic, strong) id<协议名称> 属性名;

 

 @property (nonatomic, strong) Dog<MyProtocol> *dog;

 @property (nonatomic, strong) id<MyProtocol> dog2;

 

 6.协议可用定义在单独.h文件中,也可用定义在某个类中

 1> 如果这个协议只用在某个类中,应该把协议定义在该类中

 

 2> 如果这个协议用在很多类中,就应该定义在单独文件中

 

 7.分类可用定义在单独.h.m文件中,也可用定义在原来类中

 1> 一般情况下,都是定义在单独文件

 2> 定义在原来类中的分类,只要求能看懂语法

 

 @property (nonatomic, retain) id<TicketDelegate> delegate;

 // 拥有一个代理属性

 // id代表代理的类名随便

 // 但必须遵守TicketDelegate协议

 

 

 

 

 22. OC 结构体

 

 int main()

 {

 

     NSRange(location length)

     NSPoint\CGPoint

     NSSize\CGSize

     NSRect\CGRect (CGPint CGSize)

     


    // 使用这些CGPointEqualToPointCGRectContainsPoint等函数的前提是添加CoreGraphics框架

    //


    // NextStep  Foundation



    // 比较两个点是否相同(xy

**    BOOL b = CGPointEqualToPoint(CGPointMake(10, 10), CGPointMake(10, 10));

    //CGRectEqualToRect(<#CGRect rect1#>, <#CGRect rect2#>)

    //CGSizeEqualToSize(<#CGSize size1#>, <#CGSize size2#>)



    // x (50, 150) y (40 , 90)

**    BOOL b2 = CGRectContainsPoint(CGRectMake(50, 40, 100, 50), CGPointMake(60, 45));


    NSLog(@"%d", b2);


    return 0;

}


void point()

{

    CGPoint p1 = NSMakePoint(10, 10);

    NSPoint p2 = CGPointMake(20, 20);// 最常用

    

    NSSize s1 = CGSizeMake(100, 50);

    NSSize s2 = NSMakeSize(100, 50);

    CGSize s3 = NSMakeSize(200, 60);

    

    CGRect r1 = CGRectMake(0, 0, 100, 50);

    

    CGRect r2 = { {0, 0}, {100, 90}};

    

    CGRect r3 = {p1, s2};

    

    // 使用CGPointZero等的前提是添加CoreGraphics框架

    CGRect r4 = {CGPointZero, CGSizeMake(100, 90)};

    

    

    // CGSizeZero

    // CGRectZero

    

    // 表示原点

    // CGPointZero == CGPointMake(0, 0)

    

    

**    // 将结构体转为字符串

    //NSString *str = NSStringFromPoint(p1);

    

    //NSString *str = NSStringFromSize(s3);

    

    NSString *str = NSStringFromRect(r1);

    

    NSLog(@"%@", str);

    

    

    // NSLog(@"x=%f, y=%f, width=%f, height=%f", r1.origin.x, r1.origin.y, r1.size.width, r1.size.height);

}


//CGRect myRect(CGFloat x, CGFloat y, CGFloat width, CGFloat height)

//{

//    CGRect rect;

//    rect.origin.x = x;

//    rect.origin.y = y;

//    rect.size.width = width;

//    rect.size.height = height;

//

//    return rect;

//}


void range()

{

    // @"i love oc"  // love的范围

    

    //NSRange r1 = {2, 4}; // 不用

    //NSRange r2 = {.location = 2, .length = 4};// 不用

**    //NSRange r3 = NSMakeRange(2, 4); // 掌握

    NSString *str = @"i love oc";

    

    // 查找某个字符串在str中的范围

    // 如果找不到,length=0location=NSNotFound==-1

**    NSRange range = [str rangeOfString:@"java"];

    NSLog(@"loc = %ld, length=%ld", range.location, range.length);

}

*/


 

 

 /*

 23. NSString

 

 int main()

 {

  

     NSMutableString *s1 = [NSMutableString stringWithFormat:@"my age is 10"];

     // 拼接内容到s1的后面

**     [s1 appendString:@" 11 12"];

     

     // 获取is的范围

**     NSRange range = [s1 rangeOfString:@"is"];

**     [s1 deleteCharactersInRange:range];

     

     NSString *s2 = [NSString stringWithFormat:@"age is 10"];

     

**     NSString *s3 = [s2 stringByAppendingString:@" 11 12"];

     

     

     NSLog(@"s1=%@, s2=%@", s1, s2);

     

     return 0;

 }

 

 void stringExport()

 {

     // 字符串的导出

**     [@"Jack\nJack" writeToFile:@"/Users/apple/Desktop/my.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];

     

     

     NSString *str = @"4234234";

**     NSURL *url = [NSURL fileURLWithPath:@"/Users/apple/Desktop/my2.txt"];

**     [str writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:nil];

 }

 

 void stringCreate()

 {

  

     1.字符串的创建

     

    NSString *s1 = @"jack";


    //NSString *s2 = [[NSString alloc] initWithString:@"jack"];


    NSString *s3 = [[NSString alloc] initWithFormat:@"age is %d", 10];


**    // C字符串 --> OC字符串

    NSString *s4 = [[NSString alloc] initWithUTF8String:"jack"];

**    // OC字符串 --> C字符串

    const char *cs = [s4 UTF8String];


    // NSUTF8StringEncoding 用到中文就可以用这种编码

    NSString *s5 = [[NSString alloc] initWithContentsOfFile:@"/Users/apple/Desktop/1.txt" encoding:NSUTF8StringEncoding error:nil];



    // URL : 资源路径

**    // 协议头://路径

    // file://

    // ftp://

    // http://weibo.com/a.png



    // http://www.baidu.com


**    // NSURL *url = [[NSURL alloc] initWithString:@"file:///Users/apple/Desktop/1.txt"];

**    NSURL *url = [NSURL fileURLWithPath:@"/Users/apple/Desktop/1.txt"];


**    NSString *s6 = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];

    NSLog(@"s6=\n%@", s6);




     一般都会有一个类方法跟对象方法配对

     [NSURL URLWithString:<#(NSString *)#>];

     [NSString stringWithFormat:@""];

**     [NSString stringWithContentsOfFile:<#(NSString *)#> encoding:<#(NSStringEncoding)#> error:<#(NSError *__autoreleasing *)#>];

  }

 */

 

 

 

 

 /*

 24. NSArray

 

 int main()

  {

**     // @[] 只创建不可变数组NSArray

     // 错误写法

     NSMutableArray *array = @[@"jack", @"rose"];

     

     [array addObject:@"jim"];

     


    //NSArray *array = @[@"jack", @"rose"];


    return 0;

  }


// 可变数组的基本使用

void use3()

{

    NSMutableArray *array = [NSMutableArray arrayWithObjects:@"rose", @"jim", nil];

    

**    // 添加元素

    [array addObject:[[Person alloc] init]];

    

    [array addObject:@"jack"];

    

**    // 删除元素

    //[array removeAllObjects];

**    // 删除指定的对象

    // [array removeObject:@"jack"];

    [array removeObjectAtIndex:0];

    

    // 错误写法

    // [array addObject:nil];

    

    

    NSLog(@"%@", array);

    

**    NSLog(@"%ld", array.count);

}


// 遍历数组

void use2()

{

    Person *p = [[Person alloc] init];

    

    NSArray *array = @[p, @"rose", @"jack"];

    

    //    for (int i = 0; i<array.count; i++)

    //    {

    //        NSLog(@"%@", array[i]);

    //    }

    

    // id obj代表着数组中的每一个元素

    //int i = 0;

**    //    for (id obj in array)

    //    {

    //        // 找出obj元素在数组中的位置

    //        NSUInteger i = [array indexOfObject:obj];

    //

    //        NSLog(@"%ld - %@", i, obj);

    //        //i++;

    //

    //        if (i==1)

    //        {

    //            break;

    //        }

    //    }

    

**    [array enumerateObjectsUsingBlock:

     

     // 每遍历到一个元素,就会调用一次block

     // 并且当前元素和索引位置当做参数传给block

     ^(id obj, NSUInteger idx, BOOL *stop)

     {

         NSLog(@"%ld - %@", idx, obj);

         

         

         if (idx == 0)

         {

             // 停止遍历

             *stop = YES;

         }

         

     }];

    

    

    //    void ^(myblock)(id, NSUInteger, BOOL *) = ^(id obj, NSUInteger idx, BOOL *stop)

    //    {

    //        NSLog(@"%ld - %@", idx, obj);

    //

    //

    //        if (idx == 0)

    //        {

    //            // 停止遍历

    //            *stop = YES;

    //        }

    //    };

    //

    //    for (int i = 0; i<array.count; i++)

    //    {

    //        // 用来标记是否需要停止遍历

    //        BOOL isStop = NO;

    //

    //        // 取出元素

    //        id obj = array[i];

    //

    //        myblock(obj, i, &isStop);

    //

    //

    //        if (isStop)

    //        {

    //            break;

    //        }

    //    }

    

}


void use()

{

 

     int a = 5;

     

     int ages[10] = {1, 90, 89, 17};

     

     Person *p = [[Person alloc] init];

     Person *persons[5] = {p,  [[Person alloc] init]};

 

    

    // OC数组不能存放nil

**    // OC数组只能存放OC对象、不能存放非OC对象类型,比如intstructenum

    

    // 这个array永远是空数组

    // NSArray *array = [NSArray array];

    

 

    // 1.NSArray的创建

 

    NSArray *array2 = [NSArray arrayWithObject:@"jack"];

    

    // nil是数组元素结束的标记

    NSArray *array3 = [NSArray arrayWithObjects:@"jack", @"rose", nil];

    // [array2 count];

    

    //NSArray *array4 = [NSArray arrayWithObjects:@"jack", @"rose", @"4324324", nil];

    

    // 快速创建一个NSArray对象

**    NSArray *array4 = @[@"jack", @"rose", @"4324324"];

    

    

 

**    2.NSArray的元素个数

 

    NSLog(@"%ld", array3.count);

    

    

 

**    3.NSArray中元素的访问

 

    NSLog(@"%@", [array3 objectAtIndex:1]);

    

    //array3[1];

    NSLog(@"%@", array3[0]);

}

*/






/*

25. NSSet


int main()

{

    NSMutableSet *s = [NSMutableSet set];

    

    // 添加元素

**    [s addObject:@"hack"];

    

    // 删除元素

**    // [s removeObject:(id)];

    return 0;

}


// set的基本使用

void test()

{

    NSSet *s = [NSSet set];

    

    NSSet *s2 = [NSSet setWithObjects:@"jack",@"rose", @"jack2",@"jack3",nil];

    

**    // 随机拿出一个元素

    NSString *str =  [s2 anyObject];

    

    NSLog(@"%@", str);

    

**    //NSLog(@"%ld", s2.count);

}

*/







/*

26. NSSetNSArray的对比


1> 共同点

* 都是集合,都能存放多个OC对象

* 只能存放OC对象,不能存放非OC对象类型(基本数据类型:intcharfloat等,结构体,枚举)

* 本身都不可变,都有一个可变的子类


2> 不同点

* NSArray有顺序,NSSet没有顺序

*/






/*

27. NSDictionary

 

int main()

{

    NSArray *persons = @[

                         @{@"name" : @"jack", @"qq" : @"432423423", @"books": @[@"5分钟突破iOS编程", @"5分钟突破android编程"]},

                         @{@"name" : @"rose", @"qq" : @"767567"},

                         @{@"name" : @"jim", @"qq" : @"423423"},

                         @{@"name" : @"jake", @"qq" : @"123123213"}

                         ];

    

    //

    // NSDictionary *jim = persons[2];

    

    

    //

    NSString *bookName = persons[0][@"books"][1];

    NSLog(@"%@", bookName);

    //NSArray *array = persons[0][@"books"];

    

    //NSLog(@"%@", array);

    

    // 先取出1位置对应的字典

    // 再取出字典中qq这个key对应的数据

    //NSLog(@"%@", persons[1][@"qq"]);

    

    // NSLog(@"%@", jim);

    return 0;

}


void use4()

{

    // 字典不允许有相同的key,但允许有相同的valueObject

    // 字典的无序的

    NSDictionary *dict = @{

                           @"address" : @"北京",

                           @"name" : @"jack",

                           @"name2" : @"jack",

                           @"name3" : @"jack",

                           @"qq" : @"7657567765"};

        //字典的遍历,先取出所有的key值,再根据key值进行遍历

**    //    NSArray *keys = [dict allKeys];

    //

**    //    for (int i = 0; i<dict.count; i++)

    //    {

    //        NSString *key = keys[i];

    //        NSString *object = dict[key];

    //

    //

    //        NSLog(@"%@ = %@", key, object);

    //    }

    

    

    [dict enumerateKeysAndObjectsUsingBlock:

     ^(id key, id obj, BOOL *stop) {

         NSLog(@"%@ - %@", key, obj);

         

         // *stop = YES;

     }];

}


void use3()

{

    NSMutableDictionary *dict = @{@"name" : @"jack"};

    

    

**    [dict setObject:@"rose" forKey:@"name"];

}


void use2()

{

    NSMutableDictionary *dict = [NSMutableDictionary dictionary];

    

**    // 添加键值对

    [dict setObject:@"jack" forKey:@"name"];

    

    

    [dict setObject:@"北京" forKey:@"address"];

    

    [dict setObject:@"rose" forKey:@"name"];

    

    

**    // 移除键值对

    // [dict removeObjectForKey:<#(id)#>];

    

    

    NSString *str = dict[@"name"];

    

    

    //NSLog(@"%@", str);

    

    NSLog(@"%@", dict);

    

    

    //NSLog(@"%@", @[@"jack", @"rose"]);

}


void use()

{

     //字典:

     

     //key ----> value

     //索引 ----> 文字内容

     

     //里面存储的东西都是键值对

     

 

    

**    // NSDictionary *dict = [NSDictionary dictionaryWithObject:@"jack" forKey:@"name"];

    

    

    // NSArray *keys = @[@"name", @"address"];

    // NSArray *objects = @[@"jack", @"北京"];

    

**    // NSDictionary *dict = [NSDictionary dictionaryWithObjects:objects forKeys:keys];

    

    


**     //NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:

     //@"jack", @"name",

     //@"北京", @"address",

     //@"32423434", @"qq", nil];

    

    

**    NSDictionary *dict = @{@"name" : @"jack", @"address" : @"北京"};

    

**    // id obj = [dict objectForKey:@"name"];

    

**    id obj = dict[@"name"];

    

    NSLog(@"%@", obj);

    

    

    

**    // 返回的是键值对的个数

    NSLog(@"%ld", dict.count);

}

*/






/*

28. 集合


1.NSArray\NSMutableArray

* 有序

* 快速创建(不可变):@[obj1, obj2, obj3]

* 快速访问元素:数组名[i]


2.NSSet\NSMutableSet

* 无序


3.NSDictionary\NSMutableDictionary

* 无序

* 快速创建(不可变):@{key1 : value1,  key2 : value2}

* 快速访问元素:字典名[key]

*/






/*

 29. 练习


// 计算代码行数


#import <Foundation/Foundation.h>

// 计算文件的代码行数


 // path : 文件的全路径(可能是文件夹、也可能是文件)

 // 返回值 int :代码行数


NSUInteger codeLineCount(NSString *path)

{

    // 1.获得文件管理者

    NSFileManager *mgr = [NSFileManager defaultManager];

    

    // 2.标记是否为文件夹

    BOOL dir = NO; // 标记是否为文件夹

    // 标记这个路径是否存在

    BOOL exist = [mgr fileExistsAtPath:path isDirectory:&dir];

    

    // 3.如果不存在,直接返回0

    if(!exist)

    {

        NSLog(@"文件路径不存在!!!!!!");

        return 0;

    }

    

    // 代码能来到着,说明路径存在

    

    

    if (dir)

    { // 文件夹

        // 获得当前文件夹path下面的所有内容(文件夹、文件)

        NSArray *array = [mgr contentsOfDirectoryAtPath:path error:nil];

        

        // 定义一个变量保存path中所有文件的总行数

        int count = 0;

        

        // 遍历数组中的所有子文件(夹)名

        for (NSString *filename in array)

        {

            // 获得子文件(夹)的全路径

            NSString *fullPath = [NSString stringWithFormat:@"%@/%@", path, filename];

            

            // 累加每个子路径的总行数

            count += codeLineCount(fullPath);

        }

        

        return count;

    }

    else

    { // 文件

        // 判断文件的拓展名(忽略大小写)

        NSString *extension = [[path pathExtension] lowercaseString];

        if (![extension isEqualToString:@"h"]

            && ![extension isEqualToString:@"m"]

            && ![extension isEqualToString:@"c"])

        {

            // 文件拓展名不是h,而且也不是m,而且也不是c

            return 0;

        }

        

        // 加载文件内容

        NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

        

        // 将文件内容切割为每一行

        NSArray *array = [content componentsSeparatedByString:@"\n"];

        

        // 删掉文件路径前面的/Users/apple/Desktop/iOS课堂共享/0722课堂共享/

        NSRange range = [path rangeOfString:@"/Users/apple/Desktop/iOS课堂共享/0722课堂共享/"];

        NSString *str = [path stringByReplacingCharactersInRange:range withString:@""];

        

        // 打印文件路径和行数

        NSLog(@"%@ - %ld", str, array.count);

        

        return array.count;

    }

}


int main()

{

    

    NSUInteger count = codeLineCount(@"/Users/apple/Desktop/iOS课堂共享/0722课堂共享");

    

    NSLog(@"%ld", count);

    return 0;

}


void test()

{

    NSString *str = @"jack\nrose\njim\njake";

    

    [str writeToFile:@"/Users/apple/Desktop/abc.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];

    

    

    NSArray *array = [str componentsSeparatedByString:@"\n"];

    

    for (NSString *line in array)

    {

        NSLog(@"%@", line);

    }

    

    

    //int count = codeLineCount(@"/Users/apple/Desktop/iOS课堂共享/0722课堂共享/0811/代码/04-block/04-block/main.m");

    

    //NSLog(@"count=%d", count);

}

*/


 

 

 

 


/*

 30. NSNumber

 

 #import <Foundation/Foundation.h>

 

 int main()

 {

    // @20  20包装成一个NSNumber对像

 

 

    NSArray *array = @[

 

     @{@"name" : @"jack", @"age" : @20},

     

     @{@"name" : @"rose", @"age" : @25},

     

     @{@"name" : @"jim", @"age" : @27}

     ];

     

     

**     // 将各种基本数据类型包装成NSNumber对象

     @10.5;

     @YES;

     @'A'; // NSNumber对象

     

     @"A"; // NSString对象

     

     

     

**     // age变量包装成NSNumber对象

     int age = 100;

     @(age);

     //[NSNumber numberWithInt:age];

     

     

     NSNumber *n = [NSNumber numberWithDouble:10.5];

     

     

**     double d = [n doubleValue];

     

     

     

     int a = 20;

     

**     // @"20"

     NSString *str = [NSString stringWithFormat:@"%d", a];

     NSLog(@"%d", [str intValue]);

     

     return 0;

 }

 

 void test()

 {

     NSNumber *num = [NSNumber numberWithInt:10];

     

 **    NSDictionary *dict =  @{

     @"name" : @"jack",

     

     

     @"age" : num

     

     };

     

     NSNumber *num2 = dict[@"age"];

     

     

     int a = [num2 intValue];

     

     NSLog(@"%d" , a);

 }

 */






/*

 31. NSDate

 

 int main()

 {

     // 09/10/2011

     NSString *time = @"2011/09/10 18:56";

     

**     NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

**     formatter.dateFormat = @"yyyy/MM/dd HH:mm";

     

**     NSDate *date = [formatter dateFromString:time];

     NSLog(@"%@", date);

     return 0;

     }

     

     void date2string()

     {

     NSDate *date = [NSDate date];

     

     

     // 日期格式化类

**     NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

     

     // y   M   d

     // m s   H 24)时  h12)时

**     formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";

     

**     NSString *str = [formatter stringFromDate:date];

     

     NSLog(@"%@", str);

     }

     

     void use()

     {

**     // 创建一个时间对象

     NSDate *date = [NSDate date];

     // 打印出的时候是0时区的时间(北京-8区)

     NSLog(@"%@", date);

     

**     NSDate *date2 = [NSDate dateWithTimeInterval:5 sinceDate:date];

     

     

**     // 1970开始走过的秒数

     NSTimeInterval seconds = [date2 timeIntervalSince1970];

     

**     // [date2 timeIntervalSinceNow];

 }

 */








/*

32. NSValue

 

 // NSNumber之所以能包装基本数据类型为对象,是因为继承了NSValue

 

 int main()

 {

 

**     // 结构体--->OC对象

     

     CGPoint p = CGPointMake(10, 10);

     // 将结构体转为Value对象

     NSValue *value = [NSValue valueWithPoint:p];

     

**     // value转为对应的结构体

     // [value pointValue];

     

     NSArray *array = @[value ];

     

     

     // insert code here...

     // NSLog(@"这是哥修改过的东西6");

     return 0;

 }

 */





/*

33.


 strlen函数:计算字符串长度

 1.计算的是字符数,并不是字数。一个汉字算作3个字符

 2.计算的字符不包括\0

 3.从某个地址开始数字符的个数,直到遇到\0为止

 4.strlen函数声明在string.h文件中

 

 length  点语法,OC字符串的getter方法,length方法算的是字数

 

 sizeOf(int) = 4;算的是字节数

 

 count 用在OC集合中,NSArray, NSSet, NSDictionary

 

 */




/*

 34.

 

定义c字符串的2种方式

1> 利用数组

char name[] = "itcast";

* 特点:字符串里面的字符是可以修改的

* 使用场合:字符串的内容需要经常修改


2> 利用指针

char *name = "itcast";

* 特点:字符串其实是一个常量字符串,里面的字符是不能修改

* 使用场合:字符串的内容不需要修改,而且这个字符串经常使用

*/




/*

 计算数组元素的个数:sizeOf(array)/sizeOf(int)

 

 当把一个数组当做函数参数来传递的时候,会当做指针来使用

 

 

 指针就一个作用:能够根据一个地址值,访问对应的存储空间

 

 

 sizeOf(str) 返回类型是 unsigned long  %zd

 

 

*/




/*

 

 NSString *str = @"雪飞爱自己一生一世";

 //    NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];

 //    BOOL isYes = [fm createFileAtPath:filePath contents:data attributes:nil];

 

 

 

 //    NSString *str2 = @"abc";

 //

 //    [str2 writeToFile:str atomically:YES encoding:NSUTF8StringEncoding error:nil];

 //

 //    NSLog(@"%@", str);

 

 

 自定义对象的copy都是深拷贝

 

 给实例对象赋值用nil,给类对象赋值用Nil,通用指针NULL

 

 避免使用僵尸对象的方法是,在对象被释放以后,给对象赋值为nil

 

 内存分为5个区,地址由高到低一次为:栈,堆,BSS段,数据段,代码段。栈区内存分配由高到低,堆区由低到高,

 

 

 

 

 */



/*


 Object-C有多继承吗?没有的话用什么代替?

 cocoa 中所有的类都是NSObject 的子类,多继承在这里是用protocol 委托代理来实现的

 你不用去考虑繁琐的多继承 ,虚基类的概念.ood的多态特性

  obj-c 中通过委托来实现.

 

 

 Object-C有私有方法吗?私有变量呢?

 objective-c – 类里面的方法只有两种, 静态方法和实例方法. 这似乎就不是完整的面向对象了,按照OO的原则就是一个对象只暴露有用的东西. 如果没有了私有方法的话, 对于一些小范围的代码重用就不那么顺手了. 在类里面声名一个私有方法

 @interface Controller : NSObject { NSString *something; }

 + (void)thisIsAStaticMethod;

 – (void)thisIsAnInstanceMethod;

 @end

 @interface Controller (private) -

 (void)thisIsAPrivateMethod;

 @end

 @private可以用来修饰私有变量

 Objective‐C中,所有实例变量默认都是私有的,所有实例方法默认都是公有的

 

 

 关键字const什么含义

 const意味着只读,下面的声明都是什么意思?

 const int a;

 int const a;

 const int *a;

 int * const a;

 int const * a const;

 前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

 结论:

 •; 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果

 你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清

 理的。)

 •; 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。

 •; 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

 欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时,通常需要对它进行初

 始化,因为以后就没有机会再去改变它了;

 2)对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指

 定为 const

 3)在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;

 4)对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量;

 5)对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为左值

 

 

 关键字volatile有什么含义?并给出三个不同例子?

 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到

 这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

 • 并行设备的硬件寄存器(如:状态寄存器)

 • 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

 • 多线程应用中被几个任务共享的变量

 • 一个参数既可以是const还可以是volatile吗?解释为什么。

 • 一个指针可以是volatile 吗?解释为什么。

 下面是答案:

 • 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

 • 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。

 

 

 static作用?

 函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,

 因此其值在下次调用时仍维持上次的值;

 2)在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;

 3)在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明

 它的模块内;

 4)在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;

 5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。

 

 

 #import#include的区别,@class代表什么?

 @class一般用于头文件中需要声明该类的某个实例变量的时候用到,在m文件中还是需要使用#import

 #import比起#include的好处就是不会引起重复包含

 

 

 线程和进程的区别?

 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。

 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

 

 

 堆和栈的区别?

 管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak

 申请大小:

 栈:在Windows,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

 碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出

 分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

 分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。

 

 

 Object-C的内存管理?

 1.当你使用new,alloccopy方法创建一个对象时,该对象的保留计数器值为1.当你不再使用该对象时,你要负责向该对象发送一条releaseautorelease消息.这样,该对象将在使用寿命结束时被销毁.

 2.当你通过任何其他方法获得一个对象时,则假设该对象的保留计数器值为1,而且已经被设置为自动释放,你不需要执行任何操作来确保该对象被清理.如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它.

 3.如果你保留了某个对象,你需要(最终)释放或自动释放该对象.必须保持retain方法和release方法的使用次数相等.

 

 为什么很多内置的类,如TableViewControllerdelegate的属性是assign不是retain

 循环引用

 所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:

 • 对象a创建并引用到了对象b.

 • 对象b创建并引用到了对象c.

 • 对象c创建并引用到了对象b.

 这时候bc的引用计数分别是21。当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1b不会被释放。b不释放,c的引用计数就是1c也不会被释放。从此,bc永远留在内存中。

 这种情况,必须打断循环引用,通过其他规则来维护引用关系。比如,我们常见的delegate往往是assign方式的属性而不是retain方式

 的属性,赋值不会增加引用计数,就是为了防止delegation两端产生不必要的循环引用。如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象bdelegate又是a

 如果这个delegateretain方式的,那基本上就没有机会释放这两个对象了。自己在设计使用delegate模式时,也要注意这点。

 

 

 定义属性时,什么情况使用copyassignretain

 assign用于简单数据类型,如NSInteger,double,bool,

 retaincopy用于对象,

 copy用于当a指向一个对象,b也想指向同样的对象的时候,如果用assigna如果释放,再调用bcrash,如果用copy 的方式,ab各自有自己的内存,就可以解决这个问题。

 retain 会使计数器加一,也可以解决assign的问题。

 另外:atomicnonatomic用来决定编译器生成的gettersetter是否为原子操作。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。

 加了atomicsetter函数会变成下面这样:

 if (property != newValue) {

 [property release];

 property = [newValue retain];

 }

 

 

 对象是什么时候被release的?

 引用计数为0时。

 autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。对于每一个Runloop

 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object(就是autorelease的对象)会被release。那什么是一个Runloop呢?

 一个UI事件,Timer call delegate call

 都会是一个新的Runloop

 

 

 

 iOS有没有垃圾回收?

 Objective-C 2.0也是有垃圾回收机制的,但是只能在Mac OS X Leopard 10.5 以上的版本使用。

 

 

 tableView的重用机制?

 查看UITableView头文件,会找到NSMutableArray* visiableCells,和NSMutableDictnery* reusableTableCells两个结构。visiableCells内保存当前显示的cellsreusableTableCells保存可重用的cells

 TableView显示之初,reusableTableCells为空,那么tableView dequeueReusableCellWithIdentifier:CellIdentifier返回nil。开始的cell都是通过[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]来创建,而且cellForRowAtIndexPath只是调用最大显示cell数的次数。

 比如:有100条数据,iPhone一屏最多显示10cell。程序最开始显示TableView的情况是:

 1. [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]创建10cell,并给cell指定同样的重用标识(当然,可以为不同显示类型的cell指定不同的标识)。并且10cell全部都加入到visiableCells数组,reusableTableCells为空。

 2. 向下拖动tableView,当cell1完全移出屏幕,并且cell11(它也是alloc出来的,原因同上)完全显示出来的时候。cell11加入到visiableCellscell1移出visiableCellscell1加入到reusableTableCells

 3. 接着向下拖动tableView,因为reusableTableCells中已经有值,所以,当需要显示新的cellcellForRowAtIndexPath再次被调用的时候,tableView dequeueReusableCellWithIdentifier:CellIdentifier,返回cell1cell1加入到visiableCellscell1移出reusableTableCellscell2移出visiableCellscell2加入到reusableTableCells。之后再需要显示的Cell就可以正常重用了。

 

 

 ViewController loadViewviewDidLoadviewDidUnload分别是什么时候调用的,在自定义ViewCointroller时在这几个函数中应该做什么工作?

 initloadViewviewDidLoadviewDidUnloaddealloc的关系说起

 init方法

 init方法中实例化必要的对象(遵从LazyLoad思想)

 init方法中初始化ViewController本身

 loadView方法

 view需要被展示而它却是nil时,viewController会调用该方法。不要直接调用该方法。

 如果手工维护views,必须重载重写该方法

 如果使用IB维护views,必须不能重载重写该方法

 loadViewIB构建view

 你在控制器中实现了loadView方法,那么你可能会在应用运行的某个时候被内存管理控制调用。

 如果设备内存不足的时候, view 控制器会收到didReceiveMemoryWarning的消息。

 默认的实现是检查当前控制器的view是否在使用。

 如果它的view不在当前正在使用的view hierarchy里面,且你的控制器实现了loadView方法,那么这个view将被release, loadView方法将被再次调用来创建一个新的view

 viewDidLoad方法

 viewDidLoad 此方法只有当viewnib文件初始化的时候才被调用。

 重载重写该方法以进一步定制view

 iPhone OS 3.0及之后的版本中,还应该重载重写viewDidUnload来释放对view的任何索引

 viewDidLoad后调用数据Model

 viewDidUnload方法

 当系统内存吃紧的时候会调用该方法(注:viewController没有被dealloc

 内存吃紧时,在iPhone OS 3.0之前didReceiveMemoryWarning是释放无用内存的唯一方式,但是OS 3.0及以后viewDidUnload方法是更好的方式

 在该方法中将所有IBOutlet(无论是property还是实例变量)置为nil(系统release view时已经将其release掉了)

 在该方法中释放其他与view有关的对象、其他在运行时创建(但非系统必须)的对象、在viewDidLoad中被创建的对象、缓存数据等 release对象后,将对象置为nilIBOutlet只需要将其置为nil,系统release view时已经将其release掉了)

 一般认为viewDidUnloadviewDidLoad的镜像,因为当view被重新请求时,viewDidLoad还会重新被执行

 viewDidUnload中被release的对象必须是很容易被重新创建的对象(比如在viewDidLoad或其他方法中创建的对象),不要release用户数据或其他很难被重新创建的对象

 dealloc方法

 viewDidUnloaddealloc方法没有关联,dealloc还是继续做它该做的事情

 

 

 ViewControllerdidReceiveMemoryWarning是在什么时候调用的?默认的操作是什么?

 当程序接到内存警告时View Controller将会收到这个消息:didReceiveMemoryWarning

 iOS3.0开始,不需要重载这个函数,把释放内存的代码放到viewDidUnload中去。

 这个函数的默认实现是:检查controller是否可以安全地释放它的view(这里加粗的view指的是controllerview属性),比如view本身没有superview并且可以被很容易地重建(从nib或者loadView函数)。

 如果view可以被释放,那么这个函数释放view并调用viewDidUnload

 你可以重载这个函数来释放controller中使用的其他内存。但要记得调用这个函数的super实现来允许父类(一般是UIVIewController)释放view

 如果你的ViewController保存着view的子view的引用,那么,在早期的iOS版本中,你应该在这个函数中来释放这些引用。而在iOS3.0或更高版本中,你应该在viewDidUnload中释放这些引用。

 

 

 列举Cocoa中常见的集中多线程的实现,并谈谈多线程安全的几种解决办法,一般什么地方会用到多线程?

 NSOperation NSThread

 @sychonized

 怎么理解MVC,在CocoaMVC是怎么实现的?

 MVC设计模式考虑三种对象:模型对象、视图对象、和控制器对象。模型对象代表特别的知识和专业技能,它们负责保有应用程序的数据和定义操作数据的逻辑。视图对象知道如何显示应用程序的模型数据,而且可能允许用户对其进行编辑。控制器对象是应用程序的视图对象和模型对象之间的协调者。

 ViewCotroller

 Xib

 delegatenotification区别,分别在什么情况下使用?

 KVC(Key-Value-Coding)

 KVOKey-Value-Observing

 理解KVCKVO(键--编码与键--监看)

 当通过KVC调用对象时,比如:[self valueForKey:@"someKey"]时,程序会自动试图通过几种不同的方式解析这个调用。首先查找对象是否带有 someKey 这个方法,如果没找到,会继续查找对象是否带有someKey这个实例变量(iVar),如果还没有找到,程序会继续试图调用 -(id) valueForUndefinedKey:这个方法。如果这个方法还是没有被实现的话,程序会抛出一个NSUndefinedKeyException异常错误。

 (Key-Value Coding查找方法的时候,不仅仅会查找someKey这个方法,还会查找getsomeKey这个方法,前面加一个get,或者_someKey以及_getsomeKey这几种形式。同时,查找实例变量的时候也会不仅仅查找someKey这个变量,也会查找_someKey这个变量是否存在。)

 设计valueForUndefinedKey:方法的主要目的是当你使用-(id)valueForKey方法从对象中请求值时,对象能够在错误发生前,有最后的机会响应这个请求。

 self.self什么区别?

 idnil代表什么?

 id

 idvoid *并非完全一样。在上面的代码中,id是指向struct objc_object的一个指针,这个意思基本上是说,id是一个指向任何一个继承了Object(或者NSObject)类的对象。需要注意的是id是一个指针,所以你在使用id的时候不需要加星号。比如id foo=nil定义了一个nil指针,这个指针指向NSObject的一个任意子类。而id *foo=nil则定义了一个指针,这个指针指向另一个指针,被指向的这个指针指向NSObject的一个子类。

 nil

 nilC语言的NULL相同,在objc/objc.h中定义。nil表示一个Objctive-C对象,这个对象的指针指向空(没有东西就是空)。

 

 

 内存管理 Autoreleaseretaincopyassignset方法和含义?

 1,你初始化(alloc/init)的对象,你需要释放(release)它。例如:

 NSMutableArray aArray = [[NSArray alloc] init];

 后,需要

 [aArray release];

 2,你retaincopy的,你需要释放它。例如:

 [aArray retain]

 后,需要

 [aArray release];

 3,被传递(assign)的对象,你需要斟酌的retainrelease。例如:

 obj2 = [[obj1 someMethod] autorelease];

 对象2接收对象1的一个自动释放的值,或传递一个基本数据类型(NSIntegerNSString)时: 你或希望将对象2进行retain,以防止它在被使用之前就被自动释放掉。但是在retain后,一定要在适当的时候进行释放。

 关于索引计数(Reference Counting)的问题

 retain = 索引计数(Reference Counting)

 NSArray对象会retain(retain值加一)任何数组中的对象。当NSArray被卸载(dealloc)的时候,所有数组中的对象会被执行一次释放(retain值减一)。不仅仅是NSArray,任何收集类(Collection Classes)都执行类似操作。例如NSDictionary,甚至UINavigationController

 Alloc/init建立的对象,索引计数为1。无需将其再次retain

 [NSArray array][NSDate date]方法建立一个索引计数为1的对象,但是也是一个自动释放对象。所以是本地临时对象,那么无所谓了。如果是打算在全Class中使用的变量(iVar),则必须retain它。

 缺省的类方法返回值都被执行了自动释放方法。(*如上中的NSArray)

 在类中的卸载方法”dealloc”中,release所有未被平衡的NS对象。(*所有未被autorelease,而retain值为1)

 

 

 类别的作用?

 有时我们需要在一个已经定义好的类中增加一些方法,而不想去重写该类。比如,当工程已经很大,代码量比较多,或者类中已经包住很多方法,已经有其他代码调用了该类创建对象并使用该类的方法时,可以使用类别对该类扩充新的方法。

 注意:类别只能扩充方法,而不能扩充成员变量。

 

 

 委托(举例)

 委托代理(degegate),顾名思义,把某个对象要做的事情委托给别的对象去做。那么别的对象就是这个对象的代理,代替它来打理要做的事。反映到程序中,首先要明确一个对象的委托方是哪个对象,委托所做的内容是什么。

 委托机制是一种设计模式,在很多语言中都用到的,这只是个通用的思想,网上会有很多关于这方面的介绍。

 那么在苹果开发过程中,用到委托的程序实现思想如下,我主要拿如何在视图之间传输信息做个例子。

 譬如:在两个页面(UIIview视图对象)实现传值,用委托(delegate)可以很好做到!

 方法:

 A

 @interface AUIView

 id transparendValueDelegate;

 @property(nomatic, retain) id transparendValueDelegate;

 @end

 @implemtion A

 @synthesize transparendValueDelegate

 -(void)Function

 {

 NSString* value = @”hello”;

 //让代理对象执行transparendValue动作

 [transparendValueDelegate transparendValue: value];

 }

 @end

 B

 @interface BUIView

 NSString* value;

 @end

 @implemtion B

 -(void)transparendValue:(NSString*)fromValue

 {

 value = fromValue;

 NSLog(@”the value is %@ “,value);

 }

 @end

 //下面的设置A代理委托对象为B

 //在定义AB类对象处:

 A* a = [[A alloc] init];

 B* b = [[B alloc] init];

 a. transparendValueDelegate = b;//设置对象a代理为对象b

 这样在视图AB之间可以通过委托来传值!

 下面这个例子委托有两类:

 1、一个视图类对象的代理对象为父视图,子视图用代理实现让父视图显示别的子视图

 2、同一父视图下的一个子视图为另一个子视图的代理对象,让另一个子视图改变自身背景色为给定的颜色

 ===============================================

 规范格式如下:

 @protocol TransparendValueDelegate;

 @interface AUIView

 id< TransparendValueDelegate > m_dTransparendValueDelegate;

 @property(nomatic, retain) id m_dTransparendValueDelegate;

 @end

 //代理协议的声明

 @protocol TransparendValueDelegat<NSObject>

 {

 -(void)transparendValue:(NSString*)fromValue;

 }

 

 

 

 frame bounds 区别 bound的大小改变frame 改变吗?

 frame: view在父view坐标系统中的位置和大小。(参照点是,父亲的坐标系统)

 bounds:该view在本地坐标系统中的位置和大小。(参照点是,本地坐标系统)

 

 

 1。简述push原理,push的证书和其它的右什么不一样?

 2sqlite中插入特殊字符的方法和接收到处理方法。

 3。谈谈你对数组和连表认识,还有你是怎么用他们的?

 4。冒泡算法。

 5socket编程简述

 6asihttp代码原理 ,异步请求的原理,异步请求最大数目,为什么只能这么多?

 异步请求最大数目,为什么只能这么多?

 这个数量是跟cpu有关的,并发性取决于cpu核数,每个核只能同时处理一个任务.4cpu理论上可以并发处理4个任务,如果按http来算就是4个请求,但是cpu是抢占式资源,所以一般来说并发量是要根据任务的耗时和cpu的繁忙度来计算4个左右只是个经验值你开10个短耗时的任务和几个长耗时任务的效率是不同的- -..一般来说估算这个量的最大效率估算公示是cpu核数*2-1,这个公式是当时对集群进行压测得到的结论.cpu抢占时间跟任务时长开启这个数量的线程可以最大化的榨干cpu。一个道理。cpu不可能都被抢去做connection.iOScpu密集型的消耗

 。这个大概知道就行了,也不会有人特别在意吧…cpu核数*2-1那个是做淘宝的java团队压测得到的线程最优数

 ,放在iOS上也多少适用一般来说不超过这个量就好,线程不是起的越多越好,线程数就是…cpu来决定的

 7http请求方式?

 Get post put 还有是什么来着忘了

 8uiview的圆角属性设置方法。

 m_mainImgView.layer.cornerRadius = 6;

 m_mainImgView.layer.masksToBounds = YES;

 9 masksToBounds属性的作用。(决定子layer是否被当前layer的边界剪切。默认是NO。)

 8.iOS程序运行流程{

 1. 系统调用appmain函数

 2. main函数调用UIApplicationMain.

 3. UIApplicationMain创建shared application instance, UIApplication默认的instance.

 4. UIApplicationMain读取Info.plist找到主nib文件, 加载nib,把shared application instance 设为nibowner.

 5. 通过nib文件,创建app的独立UIWindows object.

 6. 通过nib,实例化了程序的AppDelegate object.

 7. app内部启动结束,application:didFinishLaunchingWith- Options: 被设定成 wAppDelegate instance.

 8. AppDelegateUIWindow instancemakeKeyAndVisible消息, app界面展示给用户. app准备好接收用户的操作指令.

 

 9. 内存管理的方式有哪些?

 手动管理 ARC GC

 10. 怎样实现一个 singleton的类.给出思路

 这个自己写一段单例的代码吧

 

 11.什么是序列化或者Acrchiving,可以用来做什么,怎样与copy结合,原理是什么?.

 12.iphone上有两件事情要做,请问是在一个线程里按顺序做效率高还是两个线程里做效率高?为什么?

 13.runloop是什么?在主线程中的某个函数里调用了异步函数,怎么样block当前线程,且还能响应当前线程的timer事件,touch事件等.

 14.ios平台怎么做数据的持久化?coredatasqlite有无必然联系?coredata是一个关系型数据库吗?

 15.阐述一个nil对象从interface bulider产生,到载入程序运行空间,最后被释放时所经历的生命周期.

 16.notification是同步还是异步? kvo是同步还是异步?notification是全进程空间的通知吗?kvo呢?

 17.kvc是什么?kvo是什么?有什么特性?

 18.响应者链是什么?

 19.unix上进程怎么通信?

 20.timer的间隔周期准吗?为什么?怎样实现一个精准的timer?

 21.UIscrollVew用到了什么设计模式?还能再foundation库中找到类似的吗?

 22.如果要开发一个类似eclipse的软件,支持插件结构。且开放给第三方开发。你会怎样去设计它?(大概思路)

 23.Cobj-c 如何混用?

 24.以下每行代码执行后,person对象的retain count分别是多少

 Person *person = [[Person alloc] init]; // count 1

 [person retain]; // count 2

 [person release]; //count 1

 [person release]; //retain count = 1;

 25ViewController didReceiveMemoryWarning 是在什么时候被调用的?

 内存出现警告时。

 26.ios5新特性有那些?

 27.谈谈你对ARC 的认识和理解?

 这个我说不好,怕说错了。在成了误导还是自己上网查吧。

 28.

 ViewCALayer的区别?

 

*/



/*


 1.

 浅复制和深复制的区别?

 

 答案:浅层复制:只复制指向对象的指针,而不复制引用对象本身。

 深层复制:复制引用对象本身。

 意思就是说我有个A对象,复制一份后得到A_copy对象后,对于浅复制来说,AA_copy指向的是同一个内存资源,复制的只不过是是一个指针,对象本身资源

 还是只有一份,那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改,这其实违背了我们复制拷贝的一个思想。深复制就好理解了,内存中存在了

 两份独立对象本身。

 用网上一哥们通俗的话将就是:

 浅复制好比你和你的影子,你完蛋,你的影子也完蛋

 深复制好比你和你的克隆人,你完蛋,你的克隆人还活着。

 2.类别(category)的作用?继承和类别在实现中有何区别?

 答案:category 可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改。

 并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。

 类别主要有3个作用:

 (1)将类的实现分散到多个不同文件或多个不同框架中。

 (2)创建对私有方法的前向引用。

 (3)向对象添加非正式协议。

 继承可以增加,修改或者删除方法,并且可以增加属性。

 3.

 类别(category)和类扩展(extension)的区别。

 答案:categoryextensions的不同在于 后者可以添加属性。另外后者添加的方法是必须要实现的。

 extensions可以认为是一个私有的Category

 4. obc中的协议和java中的接口概念有何不同?

 

 答案:OBC中的代理有2层含义,官方定义为 formalinformal protocol。前者和Java接口一样。

 informal protocol中的方法属于设计模式考虑范畴,不是必须实现的,但是如果有实现,就会改变类的属性。

 其实关于正式协议,类别和非正式协议我很早前学习的时候大致看过,也写在了学习教程里

 ”非正式协议概念其实就是类别的另一种表达方式这里有一些你可能希望实现的方法,你可以使用他们更好的完成工作

 这个意思是,这些是可选的。比如我门要一个更好的方法,我们就会申明一个这样的类别去实现。然后你在后期可以直接使用这些更好的方法。

 这么看,总觉得类别这玩意儿有点像协议的可选协议。

 现在来看,其实protocal已经开始对两者都统一和规范起来操作,因为资料中说非正式协议使用interface修饰

 现在我们看到协议中两个修饰词:必须实现(@requied)”可选实现(@optional)”

 5.解释一下KVO KVC?

 

 答案:kvc:值编码是一种间接访问对象的属性使用字符串来标识属性,而不是通过调用存取方法,直接或通过实例变量访问的机制。

 很多情况下可以简化程序代码。apple文档其实给了一个很好的例子。

 kvo:键值观察机制,他提供了观察某一属性变化的方法,极大的简化了代码。

 具体用看到嗯哼用到过的一个地方是对于按钮点击变化状态的的监控。

 比如我自定义的一个button

 [cpp]

 [self addObserver:self forKeyPath:@"highlighted" options:0 context:nil];

 

 

 #pragma mark KVO

 

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

 {

 if ([keyPath isEqualToString:@"highlighted"] ) {

 [self setNeedsDisplay];

 }

 }

 对于系统是根据keypath去取的到相应的值发生改变,理论上来说是和kvc机制的道理是一样的。

 对于kvc机制如何通过key寻找到value

 ”当通过KVC调用对象时,比如:[self valueForKey:@"someKey"]时,程序会自动试图通过几种不同的方式解析这个调用。首先查找对象是否带有 someKey 这个方法,如果没找到,会继续查找对象是否带有someKey这个实例变量(iVar),如果还没有找到,程序会继续试图调用 -(id) valueForUndefinedKey:这个方法。如果这个方法还是没有被实现的话,程序会抛出一个NSUndefinedKeyException异常错误。

 

 (cocoachina.com注:Key-Value Coding查找方法的时候,不仅仅会查找someKey这个方法,还会查找getsomeKey这个方法,前面加一个get,或者_someKey以及_getsomeKey这几种形式。同时,查找实例变量的时候也会不仅仅查找someKey这个变量,也会查找_someKey这个变量是否存在。)

 

 设计valueForUndefinedKey:方法的主要目的是当你使用-(id)valueForKey方法从对象中请求值时,对象能够在错误发生前,有最后的机会响应这个请求。这样做有很多好处,下面的两个例子说明了这样做的好处。

 来至cocoa,这个说法应该挺有道理。

 因为我们知道button却是存在一个highlighted实例变量.因此为何上面我们只是add一个相关的keypath就行了,

 可以按照kvc查找的逻辑理解,就说的过去了。

 6.

 代理的作用?

 答案:代理的目的是改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度。

 另外一点,代理可以理解为java中的回调监听机制的一种类似。

 7.

 obc中可修改和不可以修改类型。

 

 答案:可修改不可修改的集合类。这个我个人简单理解就是可动态添加修改和不可动态添加修改一样。

 比如NSArrayNSMutableArray。前者在初始化后的内存控件就是固定不可变的,后者可以添加等,可以动态申请新的内存空间。

 8.

 我们说的obc是动态运行时语言是什么意思?

 

 答案:多态。 主要是将数据类型的确定由编译时,推迟到了运行时。

 这个问题其实浅涉及到两个概念,运行时和多态。

 简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。

 多态:不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;

 那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。

 也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。

 因此也可以说,运行时机制是多态的基础?~~~

 9.通知和协议的不同之处?

 

 答案:协议有控制链(has-a)的关系,通知没有。

 首先我一开始也不太明白,什么叫控制链(专业术语了~)。但是简单分析下通知和代理的行为模式,我们大致可以有自己的理解

 简单来说,通知的话,它可以一对多,一条消息可以发送给多个消息接受者。

 代理按我们的理解,到不是直接说不能一对多,比如我们知道的明星经济代理人,很多时候一个经济人负责好几个明星的事务。

 只是对于不同明星间,代理的事物对象都是不一样的,一一对应,不可能说明天要处理A明星要一个发布会,代理人发出处理发布会的消息后,别称B

 发布会了。但是通知就不一样,他只关心发出通知,而不关心多少接收到感兴趣要处理。

 因此控制链(has-a从英语单词大致可以看出,单一拥有和可控制的对应关系。

 10.What is push notification?

 什么是推送消息?

 

 11.Polymorphism

 关于多态性

 

 答案:多态,子类指针可以赋值给父类。

 这个题目其实可以出到一切面向对象语言中,

 因此关于多态,继承和封装基本最好都有个自我意识的理解,也并非一定要把书上资料上写的能背出来。

 最重要的是转化成自我理解。

 12.Singleton?

 对于单例的理解

 

 答案:1112题目其实出的有点泛泛的感觉了,可能说是编程语言需要或是必备的基础。

 基本能用熟悉的语言写出一个单例,以及可以运用到的场景或是你编程中碰到过运用的此种模式的框架类等。

 进一步点,考虑下如何在多线程访问单例时的安全性。

 13.

 解释一下响应链

 

 答案: 事件响应链。包括点击事件,画面刷新事件等。在视图栈内从上至下,或者从下之上传播。

 可以说点事件的分发,传递以及处理。具体可以去看下touch事件这块。因为问的太抽象化了

 严重怀疑题目出到越后面就越笼统。

 14.

 framebounds有什么不同?

 

 答案:frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父亲的坐标系统)

 bounds指的是:该view在本身坐标系统中 的位置和大小。(参照点是本身坐标系统)

 15.方法和选择器有何不同?

 

 答案:selector是一个方法的名字,method是一个组合体,包含了名字和实现.

 详情可以看apple文档。

 16.

 OBC的垃圾回收机制?

 

 答案: OBC2.0Garbage collection,但是iOS平台不提供。

 一般我们了解的objective-c对于内存管理都是手动操作的,但是也有自动释放池。

 但是差了大部分资料,貌似不要和arc机制搞混就好了。

 求更多~~

 17.什么是NSOperation queue?

 

 答案:存放NSOperation的集合类。

 操作和操作队列,基本可以看成java中的线程和线程池的概念。用于处理ios多线程开发的问题。

 网上部分资料提到一点是,虽然是queue,但是却并不是带有队列的概念,放入的操作并非是按照严格的先进现出。

 这边又有个疑点是,对于队列来说,先进先出的概念是Afunc添加进队列,Bfunc紧跟着也进入队列,Afunc先执行这个是必然的,

 但是Bfunc是等Afunc完全操作完以后,B才开始启动并且执行,因此队列的概念离乱上有点违背了多线程处理这个概念。

 但是转念一想其实可以参考银行的取票和叫号系统。

 因此对于AB先排队取票但是B率先执行完操作,我们亦然可以感性认为这还是一个队列。

 但是后来看到一票关于这操作队列话题的文章,其中有一句提到

 ”因为两个操作提交的时间间隔很近,线程池中的线程,谁先启动是不定的。

 瞬间觉得这个queue名字有点忽悠人了,还不如pool~

 综合一点,我们知道他可以比较大的用处在于可以帮组多线程编程就好了。

 18.解释一下懒汉模式?

 

 答案:懒汉模式,只在用到的时候才去初始化。

 也可以理解成延时加载。

 我觉得最好也最简单的一个列子就是tableView中图片的加载显示了。

 一个延时载,避免内存过高,一个异步加载,避免线程堵塞。

 19.是否在一个视图控制器中嵌入两个tableview控制器?

 

 答案:一个视图控制只提供了一个View视图,理论上一个tableViewController也不能放吧,

 只能说可以嵌入一个tableview视图。当然,题目本身也有歧义,如果不是我们定性思维认为的UIViewController

 而是宏观的表示视图控制者,那我们倒是可以把其看成一个视图控制者,它可以控制多个视图控制器,比如TabbarController

 那样的感觉。

 20.一个tableView是否可以关联两个不同的数据源?你会怎么处理?

 

 答案:首先我们从代码来看,数据源如何关联上的,其实是在数据源关联的代理方法里实现的。

 因此我们并不关心如何去关联他,他怎么关联上,方法只是让我返回根据自己的需要去设置如相关的数据源。

 因此,我觉得可以设置多个数据源啊,但是有个问题是,你这是想干嘛呢?想让列表如何显示,不同的数据源分区块显示?

 21.Objectc的类可以多重继承么?可以实现多个接口么?重写一个类的方式用继承好还是分类好?为什么?

 Objective-c只支持单继承,如果要实现多继承的话,可以通过类别和协议的方式来实现,cocoa 中所有的类都是NSObject 的子类,多继承在这里是用protocol 委托代理 来实现的。

 22.#import #include 又什么区别 import<> #import””又什么区别?

 答案:@class一般用于头文件中需要声明该类的某个实例变量的时候用到,在m 件中还是需要使用#import#import比起#include的好处就是不会引起交叉编译。

 23.类变量的@protected ,@private,@public,@package声明各有什么含义?

 

 

 24.id 声明的对象有什么特性?

 答案:id是个很重要的类型,是个可以指向任何类型的指针或者可以理解为指向任何未知类型的指针。

 25.MVC是什么?有什么特性?为什么在iPhone上被广泛运用?

 答案:MVC设计模式考虑三种对象:模型对象、视图对象、和控制器对象。模型对象代表 特别的知识和专业技能,它们负责保有应用程序的数据和定义操作数据的逻辑。视图对象知道如何显示应用程序的模型数据,而且可能允许用户对其进行编辑。控制 器对象是应用程序的视图对象和模型对象之间的协调者。

 26.对于语句NSString* testObject = [[NSData alloc] init];testObject 在编译时和运行时分别是什么类型的对象?

 

 27.什么是安全释放?

 

 28.为什么有些4.0独有的objectivec 函数在3.1上运行时会报错.4.0独有的类在3.1上分配内存时不会报错?分配的结果是什么?

 

 29.为什么4.0独有的c函数在3.1的机器上运行不会报错(在没有调用的情况下?)而4.0独有的类名在3.1的机器上一运行就报错?

 

 30.异常exception 怎么捕获?不同的CPU结构上开销怎样?C中又什么类似的方法?

 

 31.property中属性retain,copy,assgin的含义分别是什么?有什么区别?将其转换成getset方法怎么做?有什么注意事项?

 

 32.委托是什么?委托的property声明用什么属性?为什么?

 

 33.浅拷贝和深拷贝区别是什么?

 34.Cocoa中有虚基类的概念么?怎么简洁的实现?

 

 35.自动释放池跟GC(垃圾回收)有什么区别?iPhone上有GC么?[pool release 和[pool drain]有什么区别?

 iPhone上没有GCiPhone开发的时候没有垃圾回收机制。

 在垃圾回收环境中,release是一个空操作。因此,NSAutoreleasePool提供了drain方法,在引用计数环境中,该方法的作用等同于调用release,但在垃圾回收环境中,它会触发垃圾回收(如果自上次垃圾回收以来分配的内存大于当前的阈值)。因此,在通常情况下,您应该使用drain而不是release来销毁自动释放池。

 36.

 for(int index = 0; index < 20; index ++){

 NSString *tempStr = @”tempStr”;

 NSLog(tempStr);

 NSNumber *tempNumber = [NSNumber numberWithInt:2];

 NSLog(tempNumber);

 }

 这段代码有什么问题.?会不会造成内存泄露(多线程)?在内存紧张的设备上做大循环时自动释放池是写在循环内好还是循环外好?为什么?

 

 37.内存管理的几条原则时什么?按照默认法则.那些关键字生成的对象需要手动释放?在和property结合的时候怎样有效的避免内存泄露?

 

 38.在一个对象释放前.如果他被加到了notificationCenter .不在notificationcenterremove这个对象可能会出现什么问题?

 

 39.怎样实现一个 singleton的类.给出思路。

 

 40.什么是序列化或者Acrchiving,可以用来做什么,怎样与copy结合,原理是什么?.

 

 41.

 线程与进程的区别和联系?

 答案:

 进程和线程都是由操作系统所体会的程序运行的基本 单元,系统利用该基本单元实现系统对应用的并发性。

 程和线程的主要差别在于它们是不同的操作系统资源 管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变 量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一 些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

 42.iphone上有两件事情要做,请问是在一个线程里按顺序做效率高还是两个线程里做效率高?为什么?

 

 43.runloop是什么?在主线程中的某个函数里调用了异步函数,怎么样block当前线程,且还能响应当前线程的timer事件,touch事件等.

 

 44.ios平台怎么做数据的持久化?coredatasqlite有无必然联系?coredata是一个关系型数据库吗?

 iOS中可以有四种持久化数据的方式: 属性列表、对象归档、SQLite3Core Datacore data可以使你以图形界面的方式快速的定义app的数据模型,同时在你的代码中容易获取到它。core data提供了基础结构去处理常用的功能,例如保存,恢复,撤销和重做,允许你在app中继续创建新的任务。在使用core data的时候,你不用安装额外的数据库系统,因为core data使用内置的sqlite数据库。core data将你app的模型层放入到一组定义在内存中的数据对象。core data会追踪这些对象的改变,同时可以根据需要做相反的改变,例如用户执行撤销命令。当core data在对你app数据的改变进行保存的时候,core data会把这些数据归档,并永久性保存。

 mac os xsqlite库,它是一个轻量级功能强大的关系数据引擎,也很容易嵌入到应用程序。可以在多个平台使用,sqlite是一个轻量级的嵌入式sql数据库编程。与core data框架不同的是,sqlite是使用程序式的,sql的主要的API来直接操作数据表。

 Core Data不是一个关系型数据库,也不是关系型数据库管理系统(RDBMS)。虽然Core Dta支持SQLite作为一种存储类型,但它不能使用任意的SQLite数据库。Core Data在使用的过程种自己创建这个数据库。Core Data支持对一、对多的关系。

 45.阐述一个nil对象从interface bulider产生,到载入程序运行空间,最后被释放时所经历的生命周期.

 

 46.notification是同步还是异步? kvo是同步还是异步?notification是全进程空间的通知吗?kvo呢?

 

 47.kvc是什么?kvo是什么?有什么特性?

 48.响应者链是什么?

 49.unix上进程怎么通信?

 UNIX主要支持三种通信方式:

 1. 基本通信:主要用来协调进程间的同步和互斥

 (1)锁文件通信

 通信的双方通过查找特定目录下特定类型的文件(称锁文件)来完成进程间 对临界资源访问时的互斥;例如进程p1访问一个临界资源,首先查看是否有一个特定类型文件,若有,则等待一段时间再查找锁文件。

 (2)记录锁文件

 2. 管道通信:适应大批量的数据传递

 3. IPC    :适应大批量的数据传递

 50.timer的间隔周期准吗?为什么?怎样实现一个精准的timer?

 

 51.UIscrollVew用到了什么设计模式?还能再foundation库中找到类似的吗?(答案众多,不知道哪个是对的~~)

 模板(Template)模式,所有datasourcedelegate接口都是模板模式的典型应用,

 组合模式composition,所有的container view都用了这个模式

 观察者模式observer,所有的UIResponder都用了这个模式。

 52如果要开发一个类似eclipse的软件,支持插件结构。且开放给第三方开发。你会怎样去设计它?(大概思路)

 53.

 main()

 {

 int a[5]={1,2,3,4,5};

 int *ptr=(int *)(&a+1);

 printf(“%d,%d”,*(a+1),*(ptr-1));

 }

 答:2,5

 *(a+1)就是a[1]*(ptr-1)就是a[4],执行结果是2.5

 &a+1不是首地址+1,系统会认为加一个a数组的偏 移,是偏移了一个数组的大小(本例是5intint *ptr=(int *)(&a+1);

 ptr实际 &(a[5]),也就是a+5

 原因如下:

   &a是数组指针,其类型为 int (*)[5];

 而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。a是长度为5int数组指针,所以要加 5*sizeof(int)所以ptr实际是a[5],但是prt(&a+1)类型是不一样的(这点很重要),所以prt-1只会减去sizeof(int*)a,&a的地址是一样的,但意思不一样,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].

 54.

 以下为Windows NT下的32C++程序,请计算sizeof的值

 

 void Func ( char str[100] )

 {

 sizeof( str ) = ?

 }

 void *p = malloc( 100 );

 sizeof ( p ) = ?

 答案:这题 很常见了,Func ( char str[100] )函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等 操作,可以被修改。Windows NT 32位平台下,指针的长度(占用内存的大小)为4字节,故sizeof( str ) sizeof ( p ) 都为4

 55.

 - void*getNSString(const NSString * inputString)

 {    inputString = @”This is a main test\n”;

 return ;}

 -main(void)

 {NSString *a=@”Main“;

 NSString *aString = [NSString stringWithString:@"%@",getNSString(a)];

 NSLog(@”%@\n”, aString);}

 最后问输出的字符串:NULL,output 函数返回后,内存已经被释放。

 56.

 列举几种进程的同步机制,并比较其优缺点。

 答案:  原子操作 信号量机制    自旋锁    管程,会合,分布式系统

 

 进程之间通信的途径

 答案:共享存储系统消息传递系统管道:以文件系统为基础

 

 进程死锁的原因

 答案:资源竞争及进程推进顺序非法

 

 死锁的4个必要条

 答案:互斥、请求保持、不可剥夺、环路

 

 死锁的处理

 答案:鸵鸟策略、预防策略、避免策略、检测与解除死锁

 57.

 堆和栈的区别

 管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak

 申请大小:

 栈:在Windows,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统 预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示 overflow。因此,能从栈获得的空间较小。

 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地 址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

 碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个 问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出

 分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由 alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

 分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的 效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。

 58.

 什么是键-,键路径是什么?

 模型的性质是通过一个简单的键(通常是个字符串)来指定的。视图和控制器通过键 来查找相应的属性值。在一个给定的实体中,同一个属性的所有值具有相同的数据类型。键-值编码技术用于进行这样的查找它是一种间接访问对象属性的机制。

 键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一起的对象性 质序列。第一个键的

 性质是由先前的性质决定的,接下来每个键的值也是相对于其前面的性质。键路径使您可以以独立于模型

 实现的方式指定相关 对象的性质。通过键路径,您可以指定对象图中的一个任意深度的路径,使其指向相

 关对象的特定属性。

 59.

 cobj-c如何混用

 1obj-c的编译器处理后缀为m的文件时,可以识别obj-cc的代码, 处理mm文件可以识别obj-c,c,c++代码,但cpp文件必须只能用c/c++代码,而且cpp文件include的头文件中,也不能出现obj- c的代码,因为cpp只是cpp

 2) mm文件中混用cpp直接使用即可,所以obj-ccpp不是问题

 3)在cpp中混用obj- c其实就是使用obj-c编写的模块是我们想要的。

 如果模块以类实现,那么要按照cpp class的标准写类的定义,头文件中不能出现obj-c的东西,包括#import cocoa的。实现文件中,即类的实现代码中可以使用obj-c的东西,可以import,只是后缀是mm

 如果模块以函数实现,那么头文件要按 c的格式声明函数,实现文件中,c++函数内部可以用obj-c,但后缀还是mmm

 

 总结:只要cpp文件和cpp include的文件中不包含obj-c的东西就可以用了,cpp混用obj-c的关键是使用接口,而不能直接使用实现代码,实际上cpp混用的是 obj-c编译后的o文件,这个东西其实是无差别的,所以可以用。obj-c的编译器支持cpp.

 60.

 目标-动作机制

 目标是动作消息的接收者。一个控件,或者更为常见的是它的单元,以插座变量(参 插座变量部分)

 的形式保有其动作消息的目标。

 动作是控件发送给目标的消息,或者从目标的角度看,它是目标为了响应动作而实现 的方法。

 程序需要某些机制来进行事件和指令的翻译。这个机制就是目标-动作机制。

 61.

 cocoa touch框架

 iPhone OS 应用程序的基础 Cocoa Touch 框架重用了许多 Mac 系统的成熟模式,但是它更多地专注于触摸的接口和优化。UIKit 为您提供了在 iPhone OS 上实现图形,事件驱动程序的基本工具,其建立在和 Mac OS X 中一样的 Foundation 框架上,包括文件处理,网络,字符串操作等。

 Cocoa Touch 具有和 iPhone 用户接口一致的特殊设计。有了 UIKit,您可以使用 iPhone OS 上的独特的图形接口控件,按钮,以及全屏视图的功能,您还可以使用加速仪和多点触摸手势来控制您的应用。

 各色俱全的框架 除了 UIKit 外,Cocoa Touch 包含了创建世界一流 iPhone 应用程序需要的所有框架,从三维图形,到专业音效,甚至提供设备访问 API 以控制摄像头,或通过 GPS 获知当前位置。Cocoa Touch 既包含只需要几行代码就可以完成全部任务的强大的 Objective-C 框架,也在需要时提供基础的 C 语言 API 来直接访问系统。这些框架包括:

 Core Animation

 通过 Core Animation,您就可以通过一个基于组合独立图层的简单的编程模型来创建丰富的用户体验。

 Core Audio

 Core Audio 是播放,处理和录制音频的专业技术,能够轻松为您的应用程序添加强大的音频功能。

 Core Data

 提供了一个面向对象的数据管理解决方案,它易于使用和理解,甚至可处理任何应用 或大或小的数据模型。

 功能列表:框架分类

 下面是 Cocoa Touch 中一小部分可用的框架:

 音频和视频

 Core Audio

 OpenAL

 Media Library

 AV Foundation

 数据管理

 Core Data

 SQLite

 图形和动画

 Core Animation

 OpenGL ES

 Quartz 2D

 网络/li>

 Bonjour

 WebKit

 BSD Sockets

 用户应用

 Address Book

 Core Location

 Map Kit

 Store Kit

 62.

 objc的内存管理

 如果您通过分配和初始化(比如[[MyClass alloc] init])的方式来创建对象,您就拥

 有这个对象,需要负责该对象的释放。这个规则在使用NSObject的便利方法new 时也同样适用.

 如果您拷贝一个对象,您也拥有拷贝得到的对象,需要负责该对象的释放.

 如果您保持一个对象,您就部分拥有这个对象,需要在不再使用时释放该对象。

 如果您从其它对象那里接收到一个对象,则您不拥有该对象,也不应该释放它(这个规则有少数

 的例外,在参考文档中有显式的说明)。

 63.

 自动释放池是什么,如何工作

 当您向一个对象发送一个autorelease消息时,Cocoa就会将该对 象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置 时,自动释放池就会被释放,池中的所有对象也就被释放。

 1.  ojc-c 是通过一种”referring counting”(引用计数)的方式来管理内存的, 对象在开始分配内存(alloc)的时候引用计数为一,以后每当碰到有copy,retain的时候引用计数都会加一, 每当碰到releaseautorelease的时候引用计数就会减一,如果此对象的计数变为了0, 就会被系统销毁.

 2. NSAutoreleasePool 就是用来做引用计数的管理工作的,这个东西一般不用你管的.

 3. autoreleaserelease没什么区别,只是引用计数减一的时机不同而已,autorelease会在对象的使用真正结束的时候才做引用计数 减一.

 64.

 类工厂方法是什么

 类工厂方法的实现是为了向客户提供方便,它们将分配和初始化合在一个步骤中, 返回被创建的对象,并

 进行自动释放处理。这些方法的形式是+ (type)className…(其中 className不包括任何前缀)。

 工厂方法可能不仅仅为了方便使用。它们不但可以将分配和初始化合在一起,还可以 为初始化过程提供对

 象的分配信息。

 类工厂方法的另一个目的是使类(比如NSWorkspace)提供单件实例。虽 init…方法可以确认一

 个类在每次程序运行过程只存在一个实例,但它需要首先分配一个生的实例,然后还必须释放该实例。

 工厂 方法则可以避免为可能没有用的对象盲目分配内存。

 65.

 单件实例是什么

 Foundation Application Kit 框架中的一些类只允许创建单件对象,即这些类在当前进程中的唯一实例。举例来说,NSFileManager NSWorkspace 类在使用时都是基于进程进行单件对象的实例化。当向这些类请求实例的时候,它们会向您传递单一实例的一个引用,如果该实例还不存在,则首先进行实例的分配 和初始化。 单件对象充当控制中心的角色,负责指引或协调类的各种服务。如果类在概念上只有一个实例(比如

 NSWorkspace),就应该产生 一个单件实例,而不是多个实例;如果将来某一天可能有多个实例,您可

 以使用单件实例机制,而不是工厂方法或函数。

 66.

 动态绑定在运行时确定要调用的方法

 动态绑定将调用方法的确定也推迟到运行时。在编译时,方法的调用并不和代码绑定 在一起,只有在消实发送出来之后,才确定被调用的代码。通过动态类型和动态绑定技术,您的代码每次执行都可以得到不同的结果。运行时因子负责确定消息的接 收者和被调用的方法。 运行时的消息分发机制为动态绑定提供支持。当您向一个动态类型确定了的对象发送消息时,运行环境系统会通过接收者的isa指针定位对象的类,并以此为起点 确定被调用的方法,方法和消息是动态绑定的。而且,您不必在Objective-C 代码中做任何工作,就可以自动获取动态绑定的好处。您在每次发送消息时,

 

 特别是当消息的接收者是动态类型已经确定的对象时,动态绑定就会例行而 透明地发生。

 67.

 obj-c的优缺点

 objc优点:

 1) Cateogies

 2) Posing

 3) 动态识别

 4) 指标计算

 5)弹性讯息传递

 6) 不是一个过度复杂的 C 衍生语言

 7) Objective-C C++ 可混合编程

 

 缺点:

 1) 不支援命名空間

 2)  不支持运算符重载

 3 不支持多重继承

 4 使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优化方法都用不到。(如内联函数等),性能低劣。

 68.

 readwritereadonlyassignretaincopynonatomic 属性的作用?

 @property 一个属性访问声明,扩号内支持以下几个属性:

 1getter=getterNamesetter=setterName,设置setter getter的方法名

 2readwrite,readonly,设置可供访问级别

 2assignsetter方法直接赋值,不进行 任何retain操作,为了解决原类型与环循引用问题

 3retainsetter方法对参数进行release旧值再retain新值,所有 实现都是这个顺序(CC上有相关资料)

 4copysetter方法进行Copy操作,与retain处理流程一样,先旧值release,再 Copy出新的对象,retainCount1。这是为了减少对上下文的依赖而引入的机制。

 5nonatomic,非原子性访问,不加同步, 多线程并发访问会提高性能。注意,如果不加此属性,则默认是两个访问方法都为原子型事务访问。锁被加到所属对象实例级(我是这么理解的…)

 69.

 ViewController didReceiveMemoryWarning 是在什么时候被调用的?(87题)

 70.

 谈谈你对ARC 的认识和理解?

 

 71. ObjC中,与alloc语义相反的方法是dealloc还是release?与retain语义相反的方法是dealloc还是release,为什么?需要与alloc配对使用的方法是dealloc还是release,为什么?

 allocdealloc语意相反,alloc是创建变量,dealloc是释放变量。 retain 对应release,retain 保留一个对象。调用之后,变量的计数加1。或许不是很明显,在这有例为证:

 - (void) setName : (NSString*) name {

 [name retain];

 [myname release];

 myname = name; }

 我们来解释一下:设想,用户在调用这个函数的时候,他注意了内存的管理,所以他小心的写了如下代码:

 NSString * newname = [[NSString alloc] initWithString: @”John”];

 [aClass setName: newname];

 [newname release];

 我们来看一看newname的计数是怎么变化的。首先,它被alloccount = 1; 然后,在setName中,它被retain count = 2; 最后,用户自己释放newnamecount = 1myname指向了newname。这也解释了为什么需要调用[myname release]。我们需要在给myname赋新值的时候,释放掉以前老的变量。retain 之后直接dealloc对象计数器没有释放。alloc 需要与release配对使用,因为alloc 这个函数调用之后,变量的计数加1。所以在调用alloc 之后,一定要调用对应的release。另外,在release一个变量之后,他的值仍然有效,所以最好是后面紧接着再var = nil

 72. 在一个对象的方法里面:

 self.name<http://self.name/> = “object”;

 

 name ”object”

 有什么不同?

 73. 这段代码有什么问题:

 @implementation Person

 - (void)setAge:(int)newAge {

 self.age = newAge;

 }

 @end

 74. 什么是retain count?

 75. 以下每行代码执行后,person对象的retain count分别是多少

 Person *person = [[Person alloc] init];

 [person retain];

 [person release];

 [person release];

 76. 为什么很多内置类如UITableViewControllerdelegate属性都是assign而不是retain的?

 77. 定义属性时,什么情况使用copyassign,和retain

 assign用于简单数据类型,如NSInteger,double,bool,retain copy用户对象,copy用于当 a指向一个对象,b也想指向同样的对象的时候,如果用assigna如果释放,再调用bcrash,如果用copy 的方式,ab各自有自己的内存,就可以解决这个问题。retain 会使计数器加一,也可以解决assign的问题。另外:atomicnonatomic用来决定编译器生成的gettersetter是否为原子操作。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomicsetter函数会变成下面这样:

 if (property != newValue) {       [property release];       property = [newValue retain];   }

 78. autorelease的对象是在什么时候被release的?

 答:autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。对于每一个Runloop 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object(就是autorelease的对象)会被release。那什么是一个Runloop呢? 一个UI事件,Timer call delegate call 都会是一个新的Runloop。那什么是一个Runloop呢? 一个UI事件,Timer call delegate call 都会是一个新的Runloop

 79. 这段代码有什么问题,如何修改

 for (int i = 0; i < someLargeNumber; i++)

 {

 NSString *string = @”Abc”;

 string = [string lowercaseString];

 string = [string stringByAppendingString:@"xyz"];

 NSLog(@”%@”, string);

 }

 80. autorelease和垃圾回收机制(gc)有什么关系?IPhone OS有没有垃圾回收(gc)?

 

 81. Objective C中的protocal是什么?

 @protocal obj里面的协议就如同java里面的interface

 82. Objective C中的selector 是什么?

 你可以理解 @selector()就是取类方法的编号,他的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Objective-C的类不能直接应用函数指针,这样只能做一个@selector语法来取.

 它的结果是一个SEL类型。这个类型本质是类方法的编号(函数地址)

 83. Objective C中的category是什么?

 Objective-C提供了一个非常灵活的类(Class)扩展机制-类别(Category)。类别用于对一个已经存在的类添加方法(Methods)。你只需要知道这个类的公开接口,不需要知道类的源代码。需要注意的是,类别不能为已存在的类添加实例变量(Instance Variables)

 子类(subclassing)是进行类扩展的另一种常用方法。与子类相比,类别最主要的优点是:系统中已经存在的类可以不需修改就可使用类别的扩展功能。

 类别的第二大优点是实现了功能的局部化封装。类别定义可以放在一个已存在的类(A)的定义文件中(.h)。这意味着这个类别只有在类A被引用的前提下才会被外部看到。如果另一个类(B)不需要用到类A的功能(没有包含类A.h文件),也就不会看到依附类A存在的类别。iOS SDK中广泛运用这种类别定义法来封装功能。

 84. 什么是Notification?什么时候用delegate,什么时候用Notification

 观察者模式,controllerdefaultNotificationCenter添加自己的notification,其他类注册这个notification就可以收到通知,这些类可以在收到通知时做自己的操作(多观察者默认随机顺序发通知给观察者们,而且每个观察者都要等当前的某个观察者的操作做完才能轮到他来操作,可以用NotificationQueue的方式安排观察者的反应顺序,也可以在添加观察者中设定反映时间,取消观察需要在viewDidUnload dealloc中都要注销)。

 delegate针对one-to-one关系,并且reciever可以返回值给sendernotification 可以针对one-to-one/many/none,reciever无法返回值给sender.所以,delegate用于sender希望接受到reciever的某个功能反馈值,notification用于通知多个object某个事件。

 85. 什么是KVCKVONotificationKVO有什么不同?KVOObjC中是怎么实现的?

 86. ViewController loadView, viewDidLoad, viewDidUnload 分别是在什么时候调用的?在自定义ViewController的时候这几个函数里面应该做什么工作?

 答:viewDidLoadview nib文件初始化时调用,loadViewcontrollerviewnil时调用。此方法在编程实现view时调用,view 控制器默认会注册memory warning notification,view controller的任何view 没有用的时候,viewDidUnload会被调用,在这里实现将retain view release,如果是retainIBOutlet view 属性则不要在这里release,IBOutlet会负责release

 87. ViewController didReceiveMemoryWarning 是在什么时候被调用的?UIViewController类中didReceiveMemoryWarning默认的操作是什么?

 答:默认调用[super didReceiveMemoryWarning]

 88. UITableViewController 中,创建UITableViewCell时,initWithSytle:resuseIdentifier 中,reuseIdentifier有什么用?简述UITableViewCell的复用原理.

 复用队列的元素增加:只有在cell被滑动出界面的时候,此cell才会被加入到复用队列中。每次在创建cell的时候,程序会首先通过调用dequeueReusableCellWithIdentifier:cellType方法,到复用队列中去寻找标示符为”cellType”cell,如果找不到,返回nil,然后程序去通过调用[[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:cellType] autorelease]来创建标示符为”cellType”cell

 89. UIView CALayer 有什么区别?

 两者最大的区别是,图层不会直接渲染到屏幕上。

 90. UIView animateWithDuration:animations: 为例,简述UIView动画原理。

 

 */


/*

 

 

 

 

 

 指针就一个作用:能够根据一个地址值,访问对应的存储空间

 

 // 补齐算法(对齐算法)

 // 结构体所占用的存储空间 必须是 最大成员字节数的倍数

 

 外部函数:定义的函数可以被本文件和其他文件访问

 内部函数:定义的函数只能被本文件访问

 static对函数的作用:定义和声明一个内部函数

 extern对函数的作用:完整地定义和声明一个外部函数(可省略)


 

 变量:全局变量、局部变量

 全局变量:外部变量(定义的变量可以被本文件和其他文件访问)、内部变量(定义的变量只能被本文件访问)

 默认所有的全局变量都是外部变量,但不能加extern

 

 int b (默认是外部变量)

 static int a (修饰全部内部变量)

 int main()

 {

    return 0;

 }

 

 static对变量的作用:定义一个内部变量,修饰局部变量

 extern对变量的作用:声明一个外部变量

 

 以上是对全部变量的作用,static也可以修饰局部变量

 

 static修饰局部变量的使用场合:(某个变量调用频率很高并且值固定不变)

 1.如果某个函数的调用频率特别高

 2.这个函数内部的某个变量值是固定不变的

 

 static修饰局部变量:(延长局部变量生命周期)

 1> 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁

 2> 并没有改变局部变量的作用域

 3> 所有的test函数都共享着一个变量b

 

 {

     全局变量分2种:

     外部变量:定义的变量能被本文件和其他文件访问

     1> 默认情况下,所有的全局变量都是外部变量

     1> 不同文件中的同名外部变量,都代表着同一个变量

     

     内部变量:定义的变量只能被本文件访问,不能被其他文件访问

     1> 不同文件中的同名内部变量,互不影响

     

     static对变量的作用:

     定义一个内部变量

     

     extern对变量的作用:

     声明一个外部变量

     

     static对函数的作用:

     定义和声明一个内部函数

     

     extern对函数的作用:

     定义和声明一个外部函数(可以省略)

 }

 

 

 作用:

 1.让成员变量和get方法的名称区分开

 2.可以跟局部变量区分开,一看到下划线开头的变量,一般都是成员变量

 

 类方法的好处和使用场合(不依赖对象,执行效率高,方法内部不需要使用到成员变量时,就可以用类方法)

 1> 不依赖于对象,执行效率高

 2> 能用类方法,尽量用类方法

 3> 场合:当方法内部不需要使用到成员变量时,就可以改为类方法

 可以允许类方法和对象方法同名

 

 多态(父类类型的指针指向子类对象,如果函数或方法的参数是父类类型,可以传入父类或子类对象,但是父类类型的变量不能直接调用子类特有的方法,需要强转为子类类型才能调用)

 1.没有继承就没有多态

 2.代码的体现:父类类型的指针指向子类对象

 3.好处:如果函数\方法参数中使用的是父类类型,可以传入父类、子类对象

 4.局限性:

 1> 父类类型的变量 不能 直接调用子类特有的方法。必须强转为子类类型变量后,才能直接调用子类特有的方法

 

 2.概念

 1> 僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用

 2> 野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS

 3> 空指针 :没有指向任何东西的指针(存储的东西是nilNULL0),给空指针发送消息不会报错

 

 

 2.autorelease的好处

 1> 不用再关心对象释放的时间

 2> 不用再关心什么时候调用release

 

 3.autorelease的使用注意

 1> 占用内存较大的对象不要随便使用autorelease

 2> 占用内存较小的对象使用autorelease,没有太大影响

 

 2.开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象

 1> 创建对象时不要直接用类名,一般用self

 + (id)person

 {

 return [[[self alloc] init] autorelease];

 }

 

 

 

 1.协议的定义

 @protocol 协议名称 <NSObject>

 // 方法声明列表....

 @end

 

 

 2.如何遵守协议

 1> 类遵守协议

 @interface 类名 : 父类名 <协议名称1, 协议名称2>

 

 @end

 

 2> 协议遵守协议

 @protocol 协议名称 <其他协议名称1, 其他协议名称2>

 

 @end

 

 3.协议中方法声明的关键字

 1> @required (默认)

 要求实现,如果没有实现,会发出警告

 2> @optional

 不要求实现,怎样不会有警告

 

 4.定义一个变量的时候,限制这个变量保存的对象遵守某个协议

 类名<协议名称> *变量名;

 id<协议名称> 变量名;

 NSObject<MyProtocol> *obj;

 id<MyProtocol> obj2;

 

 如果没有遵守对应的协议,编译器会警告

 

 5.@property中声明的属性也可用做一个遵守协议的限制

 @property (nonatomic, strong) 类名<协议名称> *属性名;

 @property (nonatomic, strong) id<协议名称> 属性名;

 

 @property (nonatomic, strong) Dog<MyProtocol> *dog;

 @property (nonatomic, strong) id<MyProtocol> dog2;

 

 6.协议可用定义在单独.h文件中,也可用定义在某个类中

 1> 如果这个协议只用在某个类中,应该把协议定义在该类中

 

 2> 如果这个协议用在很多类中,就应该定义在单独文件中

 

 7.分类可用定义在单独.h.m文件中,也可用定义在原来类中

 1> 一般情况下,都是定义在单独文件

 2> 定义在原来类中的分类,只要求能看懂语法

 

 

 id instancetype的区别

 

 1.instancetype只能作为函数或方法的返回值

 2.id能作为方法或者函数的返回值、参数类型,也能用来定义变量

 instance能精确地限制返回值的具体类型

 

 [Car new]每次都会创建出一个新对象,并且会返回新对象本身(新对象的地址)

 OC是在运行过程中才会检测对象有没有实现相应的方法

 super使用场合:子类重写父类的方法时想保留父类的一些行为

 

  super使用场合:子类重写父类的方法时想保留父类的一些行为

 

 

 分类的作用:在不改变原来类内容的基础上,可以为类增加一些方法

 对成员变量(不能增加新的),对方法(可以重新实现原来类的方法,但会覆盖)

 

 类的本质:当程序启动时。。加载类分类。。加载完毕后会调用一次+load(仅一次),当第一次使用某个类时。。调用当前类的+initialize。注意都是类方法。

 

 NSLog%@ 输出一个对象(类)的时候,会调用对象的-description+description)方法,返回值NSString类型,返回结果显示在屏幕上。

 - (NSString *)description

 {

 return NSLog(@"name=%@,age=%d", _name, _age);

 }

 

 SEL _cmd

 @selector:(test);

 NSSelctorFromString:(@"test");

 NSStringFromSelector:(@selector:(test));

 [p performSelector:@selector(test)];

 

 **    // C字符串 --> OC字符串

 NSString *s4 = [[NSString alloc] initWithUTF8String:"jack"];

 **    // OC字符串 --> C字符串

 const char *cs = [s4 UTF8String];

 

 **    BOOL b = CGPointEqualToPoint(CGPointMake(10, 10), CGPointMake(10, 10));

 //CGRectEqualToRect(CGRect rect1, CGRect rect2)

 //CGSizeEqualToSize(CGSize size1, CGSize size2)

 

 **    BOOL b2 = CGRectContainsPoint(CGRectMake(50, 40, 100, 50), CGPointMake(60, 45));

 

 **    // 将结构体转为字符串

 //NSString *str = NSStringFromPoint(p1);

 //NSString *str = NSStringFromSize(s3);

 //NSString *str = NSStringFromRect(r1);

 

 **    NSRange range = [str rangeOfString:@"java"];

 

 **     [s1 appendString:@" 11 12"];

 

 // 获取is的范围

 **     NSRange range = [s1 rangeOfString:@"is"];

 **     [s1 deleteCharactersInRange:range];

 

 NSString *s2 = [NSString stringWithFormat:@"age is 10"];

 

 **     NSString *s3 = [s2 stringByAppendingString:@" 11 12"];

 

 // 遍历数组

 void use2()

 {

 Person *p = [[Person alloc] init];

 

 NSArray *array = @[p, @"rose", @"jack"];

 

 //    for (int i = 0; i<array.count; i++)

 //    {

 //        NSLog(@"%@", array[i]);

 //    }

 

 // id obj代表着数组中的每一个元素

 //int i = 0;

 **    //    for (id obj in array)

 //    {

 //        // 找出obj元素在数组中的位置

 //        NSUInteger i = [array indexOfObject:obj];

 //

 //        NSLog(@"%ld - %@", i, obj);

 //        //i++;

 //

 //        if (i==1)

 //        {

 //            break;

 //        }

 //    }

 

 **    [array enumerateObjectsUsingBlock:

 

 // 每遍历到一个元素,就会调用一次block

 // 并且当前元素和索引位置当做参数传给block

 ^(id obj, NSUInteger idx, BOOL *stop)

 {

 NSLog(@"%ld - %@", idx, obj);

 

 

 if (idx == 0)

 {

 // 停止遍历

 *stop = YES;

 }

 

 }];

 

 **    2.NSArray的元素个数

 

 NSLog(@"%ld", array3.count);

 

 NSLog(@"%@", [array3 objectAtIndex:1]);

 

 

 26. NSSetNSArray的对比

 1> 共同点

 * 都是集合,都能存放多个OC对象

 * 只能存放OC对象,不能存放非OC对象类型(基本数据类型:intcharfloat等,结构体,枚举)

 * 本身都不可变,都有一个可变的子类

 2> 不同点

 * NSArray有顺序,NSSet没有顺序

 

 

 //字典的遍历,先取出所有的key值,再根据key值进行遍历

 **    //    NSArray *keys = [dict allKeys];

 //

 **    //    for (int i = 0; i<dict.count; i++)

 //    {

 //        NSString *key = keys[i];

 //        NSString *object = dict[key];

 //

 //

 //        NSLog(@"%@ = %@", key, object);

 //    }

 

 

 [dict enumerateKeysAndObjectsUsingBlock:

 ^(id key, id obj, BOOL *stop) {

 NSLog(@"%@ - %@", key, obj);

 

 // *stop = YES;

 }];

 

 **    // 添加键值对

 [dict setObject:@"jack" forKey:@"name"];

 

 **    // 移除键值对

 // [dict removeObjectForKey:<#(id)#>];

 

 **    // id obj = [dict objectForKey:@"name"];

 **    id obj = dict[@"name"];

 

 **     NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

 **     formatter.dateFormat = @"yyyy/MM/dd HH:mm";

 **     NSDate *date = [formatter dateFromString:time];

 **     NSString *str = [formatter stringFromDate:date];

 

 **     // 结构体--->OC对象

 CGPoint p = CGPointMake(10, 10);

 // 将结构体转为Value对象

 NSValue *value = [NSValue valueWithPoint:p];

 

 **    NSLog(@"%ld", array.count);

 

 sizeof (%zd)算的是字节数,sizeof(int) = 4;

 strlen (%)的是字符数,不包括\0,一个汉字占据3个字符

 length (%)点语法,算的是字数

 count (%ld)用在OC集合中,NSArray, NSSet, NSDictionary

 


 您好,我叫顾雪飞,2014年毕业,毕业两年了,在河北邯郸上的大学,学的专业是统计,毕业上后在一个小公司,工作内容是用电脑处理一些东西,后来被调到北京工作,来北京一年了,目前还在上着班。上学期间接触过c语言,只是一些基础知识,后来经朋友介绍,我开始接触编程,从朋友那里拿了一些视频资料开始自学,感觉学到了不少东西,目前已经基本掌握了c语言和oc的知识,为了进一步提高自己,在网上看了很多培训机构,经过比较最终选择了黑马,来的目的是为了学到东西,进而在北京长期工作,让自己有个不错的发展。

 

 

 

 

 

 

 */



/*


Narray


OC数组---只能存放OC对象

OC数组不能存放nil

OC数组只能存放OC对象、不能存放非OC对象类型,比如intstructenum




这个array永远是空数组

NSArray *array = [NSArray array];


1NSArray的创建

NSArray *array2 = [NSArray arrayWithObject:@"jack"];


nil是数组元素结束的标记

NSArray *array3 = [NSArray arrayWithObjects:@"jack", @"rose", nil];

 

数组元素的个数

[array2 count];


快速创建一个NSArray对象


NSArray *array4 = @[@"jack", @"rose", @"4324324"];


 

2NSArray中元素的访问


NSLog(@"%@", [array3 objectAtIndex:1]);


数组的遍历


block遍历

[array enumerateObjectsUsingBlock:

 ^(id obj, NSUInteger idx, BOOL *stop)

 {

     NSLog(@"%ld - %@", idx, obj);

 

     if (idx == 0)

     {

         // 停止遍历

         *stop = YES;

     }

 }];


普通循环遍历,id obj代表着数组中的每一个元素

int i = 0;

for (id obj in array)

{

    // 找出obj元素在数组中的位置

    NSUInteger i = [array indexOfObject:obj];

    NSLog(@"%ld - %@", i, obj);

    i++;

 

    if (i==1)

    {

        break;

    }

}

 

 

NSSet


NSSetNSArray的对比

1> 共同点

* 都是集合,都能存放多个OC对象

* 只能存放OC对象,不能存放非OC对象类型(基本数据类型:intcharfloat等,结构体,枚举)

* 本身都不可变,都有一个可变的子类

2> 不同点

* NSArray有顺序,NSSet没有顺序

NSMutableSet *s = [NSMutableSet set];


添加元素

[s addObject:@"hack"];


删除元素

[s removeObject:@"ha"];

 

 

 

 NSDictionary

 

 key ----> value

 

 索引 ----> 文字内容

 

 里面存储的东西都是键值对

 

 字典的创建

 

 普通创建

 NSDictionary *dict = [NSDictionary dictionaryWithObject:@"jack" forKey:@"name"];


 利用数组创建

 NSArray *keys = @[@"name", @"address"];

 NSArray *objects = @[@"jack", @"北京"];

 NSDictionary *dict = [NSDictionary dictionaryWithObjects:objects forKeys:keys];

 

 

 

 快速创建

 NSDictionary *dict = @{@"name" : @"jack", @"address" : @"北京"};

 

 通过key读取字符串

 id obj = dict[@"name"];


 返回的是键值对的个数

 NSLog(@"%ld", dict.count);

 

 NSMutableDictionary的增添

 NSMutableDictionary *dict = [NSMutableDictionary dictionary];

 

 添加键值对

 [dict setObject:@"jack" forKey:@"name"];

 [dict setObject:@"北京" forKey:@"address"];

 [dict setObject:@"rose" forKey:@"name"];


 移除键值对

 [dict removeObjectForKey:@""];

 

 

 

*/





//int main()

//{

//    // 09/10/2011

//    NSString *time = @"2011/09/10 18:56";

//    

//    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

//    formatter.dateFormat = @"yyyy/MM/dd HH:mm";

//    

//    NSDate *date = [formatter dateFromString:time];

//    NSLog(@"%@", date);

//    return 0;

//}



void date2string()

{

    NSDate *date = [NSDate date];


    // 日期格式化类

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

    

    // y   M   d

    // m s   H 24)时  h12)时

    formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";

    

    NSString *str = [formatter stringFromDate:date];

    

    NSLog(@"%@", str);

}


void use()

{

    // 创建一个时间对象

    NSDate *date = [NSDate date];

    // 打印出的时候是0时区的时间(北京-8区)

    NSLog(@"%@", date);

    

    NSDate *date2 = [NSDate dateWithTimeInterval:5 sinceDate:date];

    

    

    // 1970开始走过的秒数

    NSTimeInterval seconds = [date2 timeIntervalSince1970];

    

    // [date2 timeIntervalSinceNow];

}


#import <Foundation/Foundation.h>


int main()

{

    // 将结构体转为OC对象

    

    CGPoint p = CGPointMake(10, 10);

    // 将结构体转为Value对象

    NSValue *value = [NSValue valueWithPoint:p];

    

    // value转为对应的结构体

    [value pointValue];

    

    NSArray *array = @[value ];

    

    return 0;

}


/*

 移除某个view中的所有子控件,两种方法:

 1.循环这个view中的子控件的数组,让第一个或最后一个从父控件view中移除

   while (self.某个view.subview.lastObject)

   {

        [self.某个view.subview.lastObject removeFromSuperView];

   }

 2.调用子控件数组中每个元素的执行removeFromSuperView方法,来减少自己写循环代码

   [array makeObjectsPerformSelector:@selector(removeFromSuperView)];

 

 

 让某个按钮隐藏:btn.hidden = YES;

 

 拿到label上的文字  lbl.text

 拿到按钮上的文字  [btn titleForState:]; 拿到按钮当前状态下的文字  btn.currentTitle

 

 按钮禁用 .enable  view控件禁用(切断与用户的交互) .userInteractionEnable

 

 代理:解耦

 UIAlertView通过代理来监听按钮的点击事件

 1.创建一个UIAlertView对象

 2.在创建的时候,制定代理对象(一般代理对象就是控制器)

 3.让代理对象遵守UIAlertViewDelegate

 4.让代理对象实现UIAlertViewDelegate协议中的clickButtonAtIndex:方法

 5.在该方法中,根据buttonIndex的索引来判断用户点击的是哪个按钮

 UIAlertView *alertView =

 [alertView show];

 

 动态创建某个view中的子控件(创建之后加到这个view中)

 

 UIScrollView  

 contentOffSet 一开始就显示到哪个位置

 contentInset 预留内边距,有个弹簧效果,始终有个边距回不去

 

 */




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值