IOS 면접 질문 object-c 11-20

11. self = [super init] 방법을 설명하세요.

내결함성 처리에서는 부모 클래스가 초기화에 실패하면 초기화에 실패했음을 나타내는 nil이 반환됩니다.

상속으로 인해 하위 클래스는 상위 클래스의 인스턴스와 동작을 가져야 하므로 상위 클래스를 먼저 초기화한 후 하위 클래스를 초기화해야 합니다.

12. 블록을 사용하면 어떤 장점이 있는지 간략하게 설명해주세요.
코드가 간결하고 값 전달과 콜백이 매우 편리하며 에이전트 작성을 위한 코드가 많이 절약됩니다.

특정 구현 방법인 NSTimer로 캡슐화된 블록은 다음과 같습니다.

NSTimer *timer = [NSTimerchedTimerWithTimeInterval:1.0peats
        :YES
        콜백 :^() {                 weakSelf.secondsLabel.text = ...         } [[NSRunLoop currentRunLoop] addTimer: 타이머 forMode:NSRunLoopCommonModes];


13. Objective-C에 2차원 배열이 있나요?2차원 배열을 구현하는 방법은 무엇인가요?

실제 프로젝트 개발에서는 2차원 배열도 데이터 구조로 흔히 사용됩니다 .

OC의 2차원 배열도 1차원 배열을 통해서 생성되는데, 오늘은 OC에서 2차원 배열을 어떻게 사용하는지 자세히 설명하겠습니다. NSArray
초기화된 1차원 배열과 2차원 배열은 불변 배열 입니다 .

#import
int main(int argc, const char * argv[]) { @autoreleasepool {         //2개의 1차원 배열 정의; NSArray *firstRow = [[NSArray alloc] initWithObjects:@"1",@"2",@ "3", nil]; NSArray *secondRow = [[NSArray alloc] initWithObjects:@"4",@"5",@"6", nil];         //1차원 배열을 사용하여 2차원 배열을 초기화합니다. array; NSArray * my2DArray = [[NSArray alloc] initWithObjects:firstRow,secondRow, nil];         //2차원 배열 객체 출력;         NSLog(@"two-Dimensional array: %@",my2DArray);         // 2차원 배열;         for (int i = 0; i < [ my2DArray count]; i++) {                         for (int j = 0; j < firstRow.count; j++) {                                 NSLog(@"2차원 배열 요소: %@ ",[[my2DArray objectAtIndex :i] objectAtIndex :j]);                         }                 }         }         return 0; }
        


        
        


        











14. 속성속성 수식어의 기능을 간략하게 설명해주세요.
getter =getName, setter =setName: setter 및 getter의 메소드 이름을 설정합니다.
readwrite , readonly : 액세스 수준을 설정합니다.
할당 : 원본 유형 및 순환 참조 문제를 해결하기 위해 유지 작업 없이 값을 직접 할당하는 메서드입니다.
보유 : setter 메소드는 매개변수의 이전 값을 해제한 다음 새 값을 유지합니다. 모든 구현은 이 순서를 따릅니다.

copy : setter 메소드는 보유 처리 과정과 동일하게 복사 작업을 수행하며 이전 값을 먼저 해제한 후 새 개체를 복사합니다. 보유 개수는 1입니다.

이는 상황에 대한 의존성을 줄이기 위해 도입된 메커니즘입니다.

nonatomic : 비원자 액세스, 동기화 없음, 다중 스레드 동시 액세스로 성능이 향상됩니다.

이 속성이 추가되지 않으면 기본값은 두 액세스 방법 모두 원자성 트랜잭션 액세스라는 것입니다 .

15. 형식화된 날짜 문자열 "2015-04-10"을 NSDate 유형으로 변환합니다.

NSString *timeStr = @"2015-04-10";


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


포맷터. dateFormat = @"yyyy-MM-dd";


포맷터. timeZone = [NSTimeZone defaultTimeZone];


NSDate *date = [포맷터 dateFromString:timeStr];


// 2015-04-09 16:00:00 +0000
NSLog(@"%@", date);

16、简述SDWebImage内部实现过程?
入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage 显示,然后 SDWebImageManager 根据 URL 开始处理图片。
进入 SDWebImageManager-downloadWithURL: delegate:options:userInfo:,交给 SDImageCache 从缓存查找图片是否已经下载 queryDiskCacheForKey:delegate:userInfo:.
先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示图片。
如果内存缓存中没有,生成 NSInvocationOperation 添加到队列开始从硬盘查找图片是否已经缓存

根据 URLKey 在硬盘缓存目录下尝试读取图片文件。

这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。

如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo: 。
共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。

图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。
connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。
connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理
图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。
imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。

将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存

写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。

SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。

SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。

SDWebImagePrefetcher 可以预先下载图片,方便后续使用

17、 为什么 Objective-C 的方法调用要用方括号?

首先要说的是,Objective-C 的历史相当久远,如果你查 wiki 的话,你会发现:Objective-C 和 C++ 这两种语言的发行年份都是 1983 年。在设计之初,二者都是作为 C 语言的面向对象的接班人,希望成为事实上的标准。

最后结果大家都知道了,C++ 最终胜利了,而 Objective-C 在之后的几十年中,基本上变成了苹果自己家玩的玩具。不过最终,由于 iPhone 的出现,Objective-C 迎来了第二春,在 TOBIE 语言排行榜上,从 20 名开外一路上升,排名曾经超越过 C++,达到了第三名,但是随着 swift 的出现,Objective-C 的排名则一路下滑

Objective-C 在设计之初参考了不少 Smalltalk 的设计,而消息发送则是向 Smalltalk 学来的。Objective-C 当时采用了方括号的形式来表示发送消息,为什么没有选择用点呢?

我个人觉得是,当时市面上并没有别的面向对象语言的设计参考,而 Objective-C 「发明」了方括号的形式来给对象发消息,而 C++ 则「发明」了用点的方式来 “发消息”。

有人可能会争论说 C++ 的「点」并不是真正的发消息,但是其实二者都是表示「调用对象所属的成员函数」。

另外,有读者评论说使用方括号的形式是为了向下兼容 C 语言,我并不觉得中括号是唯一选择,C++ 不也兼容了 C 语言么?Swift 不也可以调用 C 函数么?
最终,其实是 C++ 的「发明」显得更舒服一些,所以后来的各种语言都借鉴了 C++ 的这种设计,也包括 Objective-C 在内。Objective-C 2.0 版本中,引入了 dot syntax,即:
a = obj.foo 等价于 a = [obj foo]
obj.foo = 1 则等价于 [obj setFoo:1]
Objective-C 其实在设计之中确实是比较特立独行的,除了方括号的函数调用方式外,还包括比较长的,可读性很强的函数命名风格。
我个人并不讨厌 Objective-C 的这种设计,但是从 Swift 语言的设计来看,苹果也开始放弃一些 Objective-C 的特点了,比如就去掉了方括号这种函数调用方式。
所以,回到我们的问题,我个人认为,答案就是:Objective-C 在 1983 年设计的时候,并没有什么有效的效仿对象,于是就发明了一种有特点的函数调用方式,现在看起来,这种方式比点操作符还是略逊一筹。

大多数语言一旦被设计好,就很难被再次修改,应该说 Objective-C 发明在 30 年前,还是非常优秀的,它的面向对象化设计得非常纯粹,比 C++ 要全面得多,也比 C++ 要简单得多。
 18、如何创建一个可以被取消执行的 block?
// 方法一:创建一个类,将要执行的 block 封装起来,然后类的内部有一个 _isCanceled 变量,在执行的时候,检查这个变量,如果 _isCanceled 被设置成 YES 了,则退出执行。
        typedef void (^Block)();
        @interface CancelableObject : NSObject
                - (id)initWithBlock:(Block)block;
                - (void)start;
                - (void)cancel;
        @end
@implementation CancelableObject {
        BOOL _isCanceled;
        Block _block;
}
- (id) initWithBlock: (Block)block {
        self = [super init];
        if (self != nil) {
                _isCanceled = NO;
                _block = block;
        }
        return self;
}
- (void) start {
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_global_queue(0, 0),
        ^{

                if (weakSelf) {
                        typeof(self) strongSelf = weakSelf;
                        if (!strongSelf->_isCanceled) {
                                (strongSelf->_block)();
                        }
                }
        });
}

- (void) cancel {
        _isCanceled = YES;
}

@end

// 另外一种写法,将要执行的 block 直接放到执行队列中,但是让其在执行前检查另一个 isCanceled 的变量,然后把这个变量的修改实现在另一个 block 方法中,如下所示:


typedef void (^CancelableBlock)();
typedef void (^Block)();

+ (CancelableBlock) dispatch_async_with_cancelable:(Block)block {
        __block BOOL isCanceled = NO;
        CancelableBlock cb = ^() {
                isCanceled = YES;
        };

        dispatch_async(dispatch_get_global_queue(0, 0), ^{
                if (!isCanceled) {
                        block();
                }
        });

        return cb;
}

以上两种方法都只能在 block 执行前有效,如果要在 block 执行中有效,只能让 block 在执行中,有一个机制来定期检查外部的变量是否有变化,而要做到这一点,需要改 block 执行中的代码。

在本例中,如果 block 执行中的代码是通过参数传递进来的话,似乎并没有什么办法可以修改它了

19、简述IOS __block和__weak修饰符的区别?
1、__weak:
__weak只能在ARC下使用,且只能修饰对象,不能修饰基本数据类型(int等),弱引用,可以block解决循环引用问题。
2、__block:
__block既能再ARC下使用,也能在 MRC下使用。既能修饰对象又能修饰基本数据类型。
ARC、MRC区别:__block对象在ARC下可能会导致循环引用,非ARC下会避免循环引用。
ARC、MRC共同点:block内对于栈上的数据不能修改,使用__block修饰以后放到了堆上就可以修改了。
20、解释单例的写法在单例中使用数组要注意什么?
static PGSingleton *sharedSingleton;

+ (instancetype)sharedSingleton
{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
                sharedSingleton = [[PGSingleton alloc] init];
        });
        return sharedSingleton;
}
其实上面的还不是标准的单例方法,标准的单例方法需要重写 copyWithZone,allocWithZone,init,确保以任何方式创建出来的对象只有一个,这里就不详细写了。

单例使用 NSMutableArray 的时候,防止多个地方对它同时遍历和修改的话,需要加原子属性

并且property用strong,并且写一个遍历和修改的方法。

加上锁. Lock,UnLock.(PS:考虑性能问题尽量避免使用锁,在苹果的文档张看到的不要问我为什么,我也忘了自己查去。。)

추천

출처blog.csdn.net/u013491829/article/details/136570886