objective c block에 대한 이해


block 정의 변수나 데이터 구조안에 담을 수 있다. 

파라미터로 전달 할 수 있다. 반환값(return value)으로 사용할 수 있다. 

할당에 사용된 이름과 관계없이 고유한 구별이 가능하다. 

재사용해야하는 경우에는 클래스 기반의 Delegate을 사용고, 한번 사용할 로직이라면 보통 블럭을 사용 block 은 런타임에 생성되는 다이나믹 함수(Dynamic Function) 이다. 

병렬처리나 비동기처리(Asyncronous Processing), 혹은 '함수형언어(Functional Language)'의 특징과 비슷하다.


예제



#import "SampleClass.h"

#import <Foundation/Foundation.h>


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

    @autoreleasepool {

      

#pragma exam1

        // myBlock은 함수 블럭 변수이고 그 다음에 등장하는 '^(int num)'은 블럭 타입의 함수를 의미한다

        

        int multiplier = 7;

        

        //함수블럭변수 = 블럭타입의 함수;

        

        int (^myBlockVriable)(int) = ^(int myNum) {

            return myNum * multiplier;

        };

        

        //호출

        int result = myBlockVriable(11);

        NSLog(@"result : %d" , result );  //result 77

        

#pragma exam2

        //블럭 내부에서 블럭외부 참조만 할 수 있다. 읽을 수만 있지 쓸 수는 없다.

        //int x = 123;   //블럭 내부에서 쓰겠다하면 오류가 발생한다.

        __block int x = 100; //블럭 내부에서 사용하겠다 선언.

        void (^printXAndY)(int) = ^(int y) {

            x = x + y; // error

            printf(" x: %d , y:%d\n", x, y);

        };


        printXAndY(3);

        

        

#pragma exam3 -  배열에 블락삽입, 블락은 객체타입이다.

        

        NSMutableArray *temArray = [[NSMutableArray alloc]init];

        [temArray addObject:^(int i){

            NSLog(@"결과는  %d 입니다." , i);

        }

         ];

        

        void (^c)(int) = ^(int i ){

            NSLog(@"두번째 결과는  %d 입니다." , i);

        };

        [temArray addObject:c];

        

        void (^b)(int) = temArray[0];

        void (^d)(int) = temArray[1];

        

        b(5);   //결과는  5 입니다.


        d(2);  //두번째 결과는  2 입니다.



#pragma exam4 - 델리게이트 처럼 사용하기 (특정 작업이 끝났을때 자동으로 호출)

        SampleClass *sampleClass = [[SampleClass alloc]init];

        [sampleClass performActionWithCompletion:^{

            NSLog(@"performActionWithCompletion 메소드가 호출 완료된 후 후처리 메소드 작동");

        }];

        

/*

 결과:

 수행. ...   ...   ...

 performActionWithCompletion 메소드가 호출 완료된 후 후처리 메소드 작동

 */

        

        

    }

    return 0;

    

    

}




sampleclass

#import <Foundation/Foundation.h>


//블록을 쉽게 사용하기위해서 타입 설정

typedef void (^CompletionBlock)(void);


@interface SampleClass : NSObject


-(void) performActionWithCompletion : (CompletionBlock)CompletionBlock;


@end





#import "SampleClass.h"


@implementation SampleClass


- (void)performActionWithCompletion:(CompletionBlock)CompletionBlock{

    

    NSLog(@"수행 ... .... ... ");

    //이 메소드가 호출된 후 후 처리 메소드가 자동으로 호출된 곳에서 실행이 된다.

    CompletionBlock();

}


@end





UIImagePickerController를 이용한 비디오





뷰컨트롤러



#import <UIKit/UIKit.h>

#import "ImagePickerController.h"

#import "ListTableViewController.h"




@interface ViewController : UIViewController


@property(nonatomic,strong) ImagePickerController *imagePickerController;


- (IBAction)cameraBtn:(id)sender;


- (IBAction)librayBtn:(id)sender;


@end




#import "ViewController.h"


@interface ViewController ()


@end


@implementation ViewController


- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

}



- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


//카메라 띄우기

- (IBAction)cameraBtn:(id)sender {

    NSLog(@"ViewController - UIImagePickerController 선택되었습니다.");

    //UIImagePickerController 생성 + 초기화 하고

    self.imagePickerController = [ImagePickerController new];

    ImagePickerController *myCameraInstance = [_imagePickerController getCameraVC];

    

    [self presentViewController: myCameraInstance animated:YES completion:nil];

    

}


//라이브러리 띄우기

- (IBAction)librayBtn:(id)sender {

    

    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

    ListTableViewController *viewController = [[ListTableViewController alloc]init];

    [self presentViewController:viewController animated:YES completion:nil];

    

}


@end


카메라컨트롤러

/*

 앱에서 비디오 캡쳐를 하는 가장 쉬운 방법은 UIImagePickerController 이용하는 것

 UIImagePickerController를 사용하려면

 <UINavigationControllerDelegate, UIImagePickerControllerDelegate>를 구현해야한다.

 */



#import <Foundation/Foundation.h>

#import <MobileCoreServices/MobileCoreServices.h>

#import <AssetsLibrary/AssetsLibrary.h>     //사진첩 관련 api

#import <UIKit/UIKit.h>                                 //카메라 api 관련 라이브러리


@interface ImagePickerController : NSObject <UINavigationControllerDelegate,UIImagePickerControllerDelegate,UIAlertViewDelegate>



@property(strong, nonatomic) NSURL* videoRecodUrl;



//UIImagePickerController 맴버 변수

@property (strong,nonatomic) UIImagePickerController *camera;



//UIImagePickerController리턴 해주는 메소드

-(UIImagePickerController *) getCameraVC;


@end


#import "ImagePickerController.h"


@implementation ImagePickerController




//초기화

-(id)init

{

    self = [super init];

    if (self) {

        NSLog(@"ImagePickerController 초기화 시작");

        _camera = [self setupImagePickController];

        NSLog(@"ImagePickerController 초기화 완료");

    }

    return self;

}





//초기화된 UIImagePickerController객체 던져주기

-(UIImagePickerController *) getCameraVC{

    return _camera;

}



/*

* UIImagePickerController객체를 만들고

* 카메라 롤에 카메라를 저장하고 카메라를 닫는 기능 등.. 기록 된 비디오를 추가로 처리하는 델리게이트를 정의

*/


//UIImagePickerController 객체 생성 및 초기화

-(UIImagePickerController *)setupImagePickController{

    NSLog(@"ImagePickerController - setupImagePickController 진입");

    UIImagePickerController *camera;

    //비디오 레코딩을 지원하는지 확인하고

    if ([self canVideoRecoding]) {

        camera = [UIImagePickerController new];

        camera.sourceType = UIImagePickerControllerSourceTypeCamera;

        camera.mediaTypes = @[(NSString *)kUTTypeMovie];

        camera.delegate = self;

        NSLog(@"ImagePickerController - UIImagePickerController 객체 생성 + 소스타입 + 미디어 타입 + 델리게이트 설정");

    }

    return camera;

}





//카메라를 인스턴스화 하기 전에 먼저 장치에서 비디오 녹화가 지원되는지 확인


-(BOOL)canVideoRecoding

{

     NSLog(@"ImagePickerController : canVideoRecoding - 진입");

 BOOL checkCamera =  [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];

    

    NSLog(@"ImagePickerController : checkCamera : %s", checkCamera ? "true" : "false");

    //true

    

    if (checkCamera) {

       

        NSArray *canMediaType =  [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];

    

        NSLog(@"ImagePickerController : canMediaType : %@ " , canMediaType);

        //("public.image","public.movie")


        

        if ([canMediaType containsObject:(NSString *)kUTTypeMovie]) {

            NSLog(@"ImagePickerController : canVideoRecoding - 비디오 레코딩 지원됩니다.");

            return YES;

        }

        

    }

    return NO;

}



#pragma camera config Method -  참고

//카메라 구성(cameraDevice속성 을 설정)

//설정하려는 카메라가 사용가능한지 확인

- (BOOL)checkFrontCameraPicker:(UIImagePickerController *)picker

{

    NSLog(@"ImagePickerController - checkFrontCameraPicker 카메라 구성(cameraDevice속성 을 설정");

    if([UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]){

        [picker setCameraDevice:UIImagePickerControllerCameraDeviceFront];

        return YES;

    }

    return NO;

}


//UIImagePickerController는 완벽한 카메라 ui가 제공된다.

//하지만 기본 컨트롤러 위에 자신이 만든 커스텀 컨트롤러를 사용하기 원할때

- (void)configureCustomPicker:(UIImagePickerController *)picker

{

    NSLog(@"ImagePickerController - configureCustomPicker 커스텀 컨트롤러를 사용");

    

    UIView *cameraOverlay = [[UIView alloc] init];

    picker.showsCameraControls = NO;

    picker.cameraOverlayView = cameraOverlay;

}



#pragma mark - UIImagePickerControllerDelegateMethod - 카메라 델리게이트 메소드


//*참고*

//1. videoAtPathIsCompatibleWithSavedPhotosAlbum : 내가 지정한 동영상 소스의 포토 앨범에 저장 가능 여부

//2. writeVideoAtPathToSavedPhotosAlbum : 지정된 동영상 소스를 포토 앨범으로 복사

//3. completionBlock 메소드의 실행이 완료되고나서 성공 여부를 알려주는 콜백


//4.iOS 내에서 동영상 인코딩하고 포토앨범에 저장할때 사용한다.

//5.iOS 4.0 이상에서 사용 가능하다.

//6.반드시 AssetsLibrary framework 추가해야 한다.


//*저장할때 참고*

//UIImagePickerController 는 앨범열고 하나 선택해서 array 추가 다시 앨범열고 array 추가

// ALAssetsLibrary 통째로 다 가져올 수 있음



//카메라 촬영하고 자장을 눌렀을때 호출

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{

    NSLog(@"ImagePickerController - didFinishPickingMediaWithInfo 진입");

    

    //저장 위치와 카메라 객체는 맴버변수로 할당

    _videoRecodUrl = [info objectForKey:UIImagePickerControllerMediaURL];

    _camera = picker;

    

    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"저장위치" message:@"문서에 저장하시겠습니까?" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:@"NO", nil];

    [alert show];


}



//취소

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{

    NSLog(@"ImagePickerController - imagePickerControllerDidCancel");

     [picker dismissViewControllerAnimated:YES completion:nil];

}




//얼러트

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

{

    if (buttonIndex == 0)

    {

        //Code for OK button - 문서에 저장하기

        //문서위치

       NSString*documentdirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];

        NSLog(@"ImagePickerController documentdirectory : %@ ", documentdirectory);

        

        //데이트 포멧으로 파일명 지정

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

        [dateFormatter setDateFormat:@"yyyy-MM-dd_HH-mm-ss"];

         NSString *destinationPath= [documentdirectory stringByAppendingFormat:@"/my_test_video_%@.mov",[dateFormatter stringFromDate:[NSDate date]]];

        

        NSLog(@"ImagePickerController destinationPath : %@ ", destinationPath);

        

         NSError *error;

        

         //넘어온 url 위치에 있는 파일을 -> 새로 지정한 url 위치로 복사해라

        [[NSFileManager defaultManager] copyItemAtURL: _videoRecodUrl toURL:[NSURL fileURLWithPath:destinationPath] error:&error];

       

        if(error){

            NSLog(@"복사중에 에러가 발생했습니다! error copying file: %@", [error localizedDescription]);

        }

        

        //종료

         [_camera dismissViewControllerAnimated:YES completion:nil];

        

        

        

    }

    //사진첩에 저장하기

    if (buttonIndex == 1)

    {

        

        ALAssetsLibrary *libray = [[ALAssetsLibrary alloc]init];

        

        NSLog(@"ImagePickerController - videoRecodUrl  : %@" ,_videoRecodUrl);

        

        //내가 지정한 동영상 소스의 포토 앨범에 저장 가능 여부

        BOOL checkSaveVideo = [libray videoAtPathIsCompatibleWithSavedPhotosAlbum:_videoRecodUrl];

        NSLog(@"ImagePickerController : checkSaveVideo : %s", checkSaveVideo ? "true" : "false");

        

        if (checkSaveVideo) {

            [libray writeVideoAtPathToSavedPhotosAlbum:_videoRecodUrl completionBlock:^(NSURL *assetURL, NSError *error) {

                NSLog(@"assetURL : %@" , assetURL);

                

            }];

        }

        [_camera dismissViewControllerAnimated:YES completion:nil];

    }

}




@end

파일리스트


#import <UIKit/UIKit.h>

#import "VideoCell.h"

#import <MediaPlayer/MediaPlayer.h>


@interface ListTableViewController : UITableViewController{

MPMoviePlayerController *mp;

}

@property(strong,nonatomic) NSMutableArray * videoList;


@end



#import "ListTableViewController.h"


@interface ListTableViewController ()


@end


@implementation ListTableViewController

@synthesize videoList;




- (id)init

{

    self = [super initWithStyle:UITableViewStylePlain];

    

    if (self) {

        NSFileManager *fm = [NSFileManager defaultManager];

        //다큐먼트 폴더가 존재하는지 확인

        NSArray *DocumentDrectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

        NSLog(@"DocumentDrectory : %@" , DocumentDrectory[0]);

        

        NSError *error;

        [fm contentsOfDirectoryAtPath:@"/" error:&error];

        NSLog(@"디렉토리 내용 : %@ ",   [fm contentsOfDirectoryAtPath: DocumentDrectory[0] error:&error]);

        

        videoList = [[NSMutableArray alloc]initWithArray: [fm contentsOfDirectoryAtPath: DocumentDrectory[0] error:&error]];

        

        NSLog(@"초기화 완료, 개수 : %lu" , [videoList count]);

        

    }

    

    return self;

}


- (void)viewDidLoad {

    [super viewDidLoad];

    NSLog(@"ListTableViewController -  viewDidLoad");

}


- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


#pragma mark - Table view data source


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

#warning Incomplete implementation, return the number of sections

    return 1;

}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

#warning Incomplete implementation, return the number of rows

    return [videoList count];

}




- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    

    static NSString *CellIdentifier = @"VideoCell";

    

    // make a video cell object or reuse one

    VideoCell *cell = (VideoCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    

    if (cell == nil) {

        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"VideoCell" owner:self options:nil];

        cell = (VideoCell *)[nib objectAtIndex:0];

        

    }

    

    NSString *name  = [self.videoList objectAtIndex:indexPath.row];

    NSLog(@"file name: %@" , name);

    

    cell.textLabel.text = name;

    

    return cell;

}



//눌렀을때 재생

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

     NSString *name = [self.videoList objectAtIndex:indexPath.row];

     NSString *slash = @"/";

    

    NSString *newName= [slash stringByAppendingString:name];

    

    NSLog(@"newName : %@" , newName);

    

    //파일이 존재하는지 확인1

    NSFileManager *fm = [NSFileManager defaultManager];

    NSArray *DocumentDrectory2 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *path2 = DocumentDrectory2[0];

    NSString *videoFilePath = [path2 stringByAppendingString:newName];

    

    NSLog(@"오픈할 파일 경로 :  %@" , videoFilePath );

    //파일이 존재하는 지 확인2

    if ([fm fileExistsAtPath: videoFilePath]) {

        NSLog(@"파일이 존재합니다.");


        NSURL *videoURL = [NSURL fileURLWithPath:videoFilePath];

        mp = [[MPMoviePlayerViewController alloc] initWithContentURL: videoURL];

        [self presentMoviePlayerViewControllerAnimated:mp];

    }else{

        NSLog(@"파일이 존재하지 않습니다.");

    }

    

}


@end


테이블셀


#import <UIKit/UIKit.h>


@interface VideoCell : UITableViewCell


@end



#import "VideoCell.h"


@implementation VideoCell


- (void)awakeFromNib {

    [super awakeFromNib];

    // Initialization code

}


- (void)setSelected:(BOOL)selected animated:(BOOL)animated {

    [super setSelected:selected animated:animated];


    // Configure the view for the selected state

}


@end


로그


1. 뷰컨트롤러에 버튼 두개 만들기


2. info.plist 에 카메라, 오디오 녹음, 사진 라이브러리 관련 권한 주기


3. 라이브러리 추가하기 AssetsLibray, MobileCoreService, AVFoundation,MediaPlayer


4.이미지 피커 컨트롤러 생성


- 필요한 라이브러리 임포트

-델리게이트 상속


5. 뷰컨트롤러에서 버튼 만들고 카메라 컨트롤러 띄우기







**********************작동별 로그**************


<카메라를 열었을때>


ViewController - UIImagePickerController 선택되었습니다.



ImagePickerController 초기화 시작

ImagePickerController : canVideoRecoding - 진입 - 초기화 시작전 카메라 권한체크!


ImagePickerController : checkCamera : true

ImagePickerController : canMediaType : (

"public.image",

"public.movie"

)


ImagePickerController : canVideoRecoding - 비디오 레코딩 지원됩니다.


ImagePickerController - setupImagePickController 진입


ImagePickerController - UIImagePickerController의 객체 생성 + 소스타입 + 미디어 타입 + 델리게이트 설정


ImagePickerController 초기화 완료



<취소했을때>


ImagePickerController - imagePickerControllerDidCancel





<녹화후 저장했을 때>


ImagePickerController - didFinishPickingMediaWithInfo 진입


magePickerController - videoRecodUrl  : file:///private/var/mobile/Containers/Data/Application/3B7B8C2A-0432-4101-8D20-417999ED8AA8/tmp/56584213032__5440B498-E9B0-4A26-BB1F-5E6A489C1FD8.MOV


ImagePickerController : checkSaveVideo : true


assetURL : assets-library://asset/asset.MOV?id=DDFBBA97-C0F6-45B8-96A8-C7BB46ED031D&ext=MOV

UIImagePickerControllerExample.zip

예제파일



ios filemanager 관련 모듈



#import <Foundation/Foundation.h>


@interface IDFileManager : NSObject


- (NSURL *) tempFileURL;

- (void) removeFile:(NSURL *)outputFileURL;

- (void) copyFileToDocuments:(NSURL *)fileURL;

- (void) copyFileToCameraRoll:(NSURL *)fileURL;


@end





#import "IDFileManager.h"

#import <AssetsLibrary/AssetsLibrary.h>


@implementation IDFileManager


//현재 파일 위치

- (NSURL *)tempFileURL

{

    NSString *path = nil;

    NSFileManager *fm = [NSFileManager defaultManager];

    NSInteger i = 0;

    while(path == nil || [fm fileExistsAtPath:path]){

        path = [NSString stringWithFormat:@"%@output%ld.mov", NSTemporaryDirectory(), (long)i];

        i++;

    }

    return [NSURL fileURLWithPath:path];

}


//파일 삭제

- (void) removeFile:(NSURL *)fileURL

{

    NSString *filePath = [fileURL path];

    NSFileManager *fileManager = [NSFileManager defaultManager];

    if ([fileManager fileExistsAtPath:filePath]) {

        NSError *error;

        [fileManager removeItemAtPath:filePath error:&error];

        if(error){

            NSLog(@"error removing file: %@", [error localizedDescription]);

        }

    }

}


//다큐먼트에 기록

- (void) copyFileToDocuments:(NSURL *)fileURL

{

    NSLog(@"IDFileManager copyFileToDocuments - 다큐먼트에 기록시작");

    NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];

    

    NSLog(@"IDFileManager documentsDirectory : %@ ", documentsDirectory);

    

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

    

    [dateFormatter setDateFormat:@"yyyy-MM-dd_HH-mm-ss"];

    

    NSString *destinationPath = [documentsDirectory stringByAppendingFormat:@"/output_%@.mov", [dateFormatter stringFromDate:[NSDate date]]];

    

    NSLog(@"IDFileManager destinationPath : %@ ", destinationPath);

    

    

    NSError *error;

    

    //넘어온 url 위치에 있는 파일을 -> 새로 지정한 url 위치로 복사해라

    [[NSFileManager defaultManager] copyItemAtURL:fileURL toURL:[NSURL fileURLWithPath:destinationPath] error:&error];

    

    

    if(error){

        NSLog(@"error copying file: %@", [error localizedDescription]);

    }

}


//카메로 롤로 파일 저장

- (void)copyFileToCameraRoll:(NSURL *)fileURL

{

    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

    if(![library videoAtPathIsCompatibleWithSavedPhotosAlbum:fileURL]){

        NSLog(@"video incompatible with camera roll");

    }

    

    NSLog(@"IDFileManager copyFileToCameraRoll - 사진첩에 기록 시작");

    [library writeVideoAtPathToSavedPhotosAlbum:fileURL completionBlock:^(NSURL *assetURL, NSError *error) {

        

        if(error){

            NSLog(@"Error: Domain = %@, Code = %@", [error domain], [error localizedDescription]);

        } else if(assetURL == nil){

            

            //It's possible for writing to camera roll to fail, without receiving an error message, but assetURL will be nil

            //Happens when disk is (almost) full

            NSLog(@"Error saving to camera roll: no error message, but no url returned");

            

        } else {

            //remove temp file

            NSError *error;

             NSLog(@"IDFileManager copyFileToCameraRoll - 파일 매니져 removeItemAtURL-remove temp file ");

            [[NSFileManager defaultManager] removeItemAtURL:fileURL error:&error];

            

            if(error){

                NSLog(@"error: %@", [error localizedDescription]);

            }

            

        }

    }];


}



@end








ios 사이드 메뉴



MMDrawerController-master 라이브러리와 stroyboard 를 이용한 사이드 메뉴 샘플

MMDrawerController-master - stroyboard.zip



uiview 를이용한 사이드 메뉴 샘플

uiview를이용한 사이드바.zip



uiview 모듈을 이용한 사이드 메뉴 샘플

uiviewcomponent를이용한 사이드바.zip



ios objective c sha256 해쉬 sha 512 함수


#import <CommonCrypto/CommonDigest.h>


-(NSString*) sha256:(NSString *)clear{

    

    const char *s=[clear cStringUsingEncoding:NSASCIIStringEncoding];

   

    NSData *keyData=[NSData dataWithBytes:s length:strlen(s)];

    

    uint8_t digest[CC_SHA256_DIGEST_LENGTH]={0};

    CC_SHA256(keyData.bytes, keyData.length, digest);

    NSData *out=[NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];

    NSString *hash=[out description];

    hash = [hash stringByReplacingOccurrencesOfString:@" " withString:@""];

    hash = [hash stringByReplacingOccurrencesOfString:@"<" withString:@""];

    hash = [hash stringByReplacingOccurrencesOfString:@">" withString:@""];

    return hash;

}



-(NSString *)createSHA512:(NSString *)string

{

    const char *cstr = [string cStringUsingEncoding:NSUTF8StringEncoding];

    NSData *data = [NSData dataWithBytes:cstr length:string.length];

    uint8_t digest[CC_SHA512_DIGEST_LENGTH];

    CC_SHA512(data.bytes, data.length, digest);

    NSMutableString* output = [NSMutableString  stringWithCapacity:CC_SHA512_DIGEST_LENGTH * 2];

    

    for(int i = 0; i < CC_SHA512_DIGEST_LENGTH; i++)

        [output appendFormat:@"%02x", digest[i]];

    return output;

}



ios 카메라를 만드는 두가지 방법 1



  1. UIImagePickerController 사용

-Foundation 프레임워크의 UIImagePickerController 클래스를 사용하면된다.


장점: 구현이 쉽다.

단점: 커스터마이징이 어렵다.



  1. AVCaptureDevice
  • 다양한 커스터마이징 가능



AVCaptureSession

카메라를 시작을 선언해주는 역할을 한다.

일반적으로 별도의 카메라 큐를 만들어서 세션을 시작한다.




AVCaptureVideoPreviewLayer

카메라가 향하는 곳을 영상으로 있다.

PreViewLayer 영상이 나오는 화면을 가르킨다.

PreViewLayer  AVCaptureDevice + AVCaptureSession + Input + Output 갖춰야 한다.



AVCaptureDeviceInput 

입력을 담당하는 부분


AVCaptureStillImageOutput

출력을 담당하는 부분


AVCaptureDevicePosition

입출력을 담당할 카메라가 전면인지 후면인지 지정하지 않았는지를 구분.



AVCaptureVideoVideoOrientation

폰이 세워져 있는지 거꾸로 있는지, 누워있는지 구분한다


세워져있을때 : Portrait

세워서 거꾸로: PortraitUpsideDown

누워있는지 : LandscapeRight, LandscapeLeft

PrevieLayer connection.videoOrientation 프로퍼티에 지정해주면된다.





AVAuthorizationStatus

카메라를 구동전, 카메라 세션을 시작하기전에 먼저 사용자로 부터 사용허가를 받았는지 체크해준다.

정의 안됨 notDetermined, 제한 Restricted, 거부당함 Denied, 허가 받음 Authorized




sessionPreset

카메라 세션의 해상도 설정



videoGravity

AVCapturePreviewLayer property 이다 . PreviewLayer 사이즈를 조정하는 역할을 한다.



captureStillImageAsynchronouslyFromConnection

AVCaptureStillImageOutput 메서드의 하나로 프리뷰 영상을 스냅샷해서 버퍼로 만들고 이미지화 한다.



jpegStillImageNSDataRepresentation

AVCaptureStillImageOutput 메소드 하나로 메서드에서 생성한 버퍼를 이미지 데이터로 변환한다.

이미지는 uiimage 만들 있고, cgimage 전환했다가 다시 uIimage 변환 가능



AVAssetsLibrary

AssetsLibrary 프레임워크의 ALAssetsLibrary 클래스는 자칭의 사진라이브러리로 접근 있도록 한다.














첫번째 방법














//

//  ViewController.h

//  CarmeraExam1

//


//


#import <UIKit/UIKit.h>

#import <AVFoundation/AVFoundation.h>

@interface ViewController : UIViewController


//프레임을 강제로 연속 캡쳐

@property (weak, nonatomic) IBOutlet UIView *frameforcapture;


//순간적으로 찍은 이미지 보이게

@property (weak, nonatomic) IBOutlet UIImageView *imageV;


- (IBAction)takePhoto:(id)sender;


@end




//

//  ViewController.m

//  CarmeraExam1

//



//


#import "ViewController.h"


@interface ViewController ()

@end


@implementation ViewController


//카메라 작동 시작을 선언!

AVCaptureSession *session;

//카메라 출력 담당

AVCaptureStillImageOutput *StilImageOutput;

//AVCapturePhotoOutput



- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

}



- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}



//뷰가 나타낼때

-(void)viewWillAppear:(BOOL)animated{

    

    //카메라를 작동시킬 세션 초기화

    session = [[AVCaptureSession alloc]init];

    [session setSessionPreset:AVCaptureSessionPresetPhoto];

    

    //디바이스 설정

    AVCaptureDevice *inputDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    NSError *error;

    //입력을 담당

    AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:&error];

    

    //카메라 세션에 입력을 담당하는 AVCaptureDeviceInput 넣어줌

    if ([session canAddInput:deviceInput]) {

        [session addInput:deviceInput];

    }

    

    //영상이 나올 화면을 위에서 만든 session 으로 초기화

    AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:session];

   

    //PreviewLayer의 사이즈를 조정하는 역할을 한다.

    [previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];

    

    

    //루트 레이어 설정

    CALayer *rootLayer = [[self view] layer];

    [rootLayer setMasksToBounds:YES];

    

    //현재 연속으로 캡쳐할 프레임 객체 생성

    CGRect frame = _frameforcapture.frame;

    

    //미리보기 레이어에 연속으로 캡쳐할 프레임 객체 넣어주고

    [previewLayer setFrame:frame];

    

    //루트레이어에 미리보기 레이어 넣어줌

    [rootLayer insertSublayer:previewLayer above:0];   //rootLayer -> previewLayer -> frame

    

    //이미지를 출력할 객체 생성 초기화

    StilImageOutput = [[AVCaptureStillImageOutput alloc]init];

    NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecTypeJPEG,AVVideoCodecKey, nil];

    [StilImageOutput setOutputSettings:outputSettings];

    

    [session addOutput:StilImageOutput];

    

    //세션 시작

    [session startRunning];

    

}



- (IBAction)takePhoto:(id)sender {

    AVCaptureConnection *videoConnection = nil;

    for (AVCaptureConnection *connection in StilImageOutput.connections) {

        for (AVCaptureInputPort *port in [connection inputPorts]) {

            if ([[port mediaType] isEqual:AVMediaTypeVideo]) {

                videoConnection = connection;

                break;

            }

        }

    }

   

    

    [StilImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef  _Nullable imageDataSampleBuffer, NSError * _Nullable error) {

        

        if (imageDataSampleBuffer != NULL) {

            //이미지 데이터 버퍼가 존재한다면

            NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];

            

            UIImage *image = [UIImage imageWithData:imageData];

            self->_imageV.image = image;

        

            /*jpegStillImageNSDataRepresentation

             AVCaptureStillImageOutput의 메소드 하나로 위 메서드에서 생성한 버퍼를 이미지 데이터로 변환한다.

             이 이미지는 uiimage 로 만들 수 있고, cgimage로 전환했다가 다시 uIimage로 변환 가능*/

        }

        

    }];


}

@end


두번째 방법




//

//  ViewController.h


//


#import <UIKit/UIKit.h>


//카메라를 구현하기 위한 델리게이트 상속

@interface ViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate>


@property (strong, nonatomic) IBOutlet UIImageView *imageHolder;



- (IBAction)cameraButtonClicked:(id)sender;


- (IBAction)localButtonClicked:(id)sender;


- (IBAction)saveButtonClicked:(id)sender;

@end



//

//  ViewController.m

//  CarmeraExam2


//


#import "ViewController.h"


@interface ViewController ()


@end


@implementation ViewController


@synthesize imageHolder;


- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    NSLog(@"viewDidLoad 호출");

}


- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    

}



- (IBAction)cameraButtonClicked:(id)sender {

    NSLog(@"cameraButtonClicked 호출");

    //이미지 피커 컨트롤러 호출

    UIImagePickerController *pickerCtrl = [[UIImagePickerController alloc]init];

    [pickerCtrl setDelegate:self];

    //편집허용

    [pickerCtrl setAllowsEditing:YES];

    [pickerCtrl setSourceType:UIImagePickerControllerSourceTypeCamera];

    [self presentViewController:pickerCtrl animated:YES completion:nil];

}


//사진 라이브러리 호출

- (IBAction)localButtonClicked:(id)sender {

     NSLog(@"localButtonClicked 호출");

    //이미지 피커 컨트롤러 호출

    UIImagePickerController *pickerCtrl = [[UIImagePickerController alloc]init];

    [pickerCtrl setDelegate:self];

    //편집 허용

    [pickerCtrl setAllowsEditing:YES];

    [pickerCtrl setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];

    [self presentViewController:pickerCtrl animated:YES completion:nil];

}


//저장

- (IBAction)saveButtonClicked:(id)sender {

    

    

    UIAlertController *alert= [UIAlertController alertControllerWithTitle:@"Yes or No?"

                                                                    message:@"사진첩에 저장하시겠습니까?"

                                                             preferredStyle:UIAlertControllerStyleAlert];


    UIAlertAction* ok = [UIAlertAction actionWithTitle:@"네"

                                                 style:UIAlertActionStyleDefault

                                              handler:^(UIAlertAction * action)

                                              {

                                                        UIImage *pickedImage = self->imageHolder.image;

                                                         UIImageWriteToSavedPhotosAlbum(pickedImage, nil, nil, nil);

                                                         NSLog(@"사진첩에 저장되었습니다.");

                                                        [self->imageHolder setImage:[UIImage imageNamed:@"empty.png"]];

                                                  }];

    

    

    UIAlertAction* cancel = [UIAlertAction actionWithTitle:@"아니요."

                                                     style:UIAlertActionStyleDefault

                              handler:^(UIAlertAction * action)

                                                      {

                                                          }];

     

    [alert addAction:ok];

    [alert addAction:cancel];

     

    [self presentViewController:alert animated:YES completion:nil];



}


#pragma mark UIImagePickerViewControllerDelegate

//이미지 피커뷰 컨트롤러 델리게이트 메소드


//카메라로 사진찍고 선택누르기

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{

    //successfully picked   / handed over

    NSLog(@"imagePickerController 호출");

    UIImage *pickedImage = info[UIImagePickerControllerEditedImage];

    [imageHolder setImage:pickedImage];

    

     //저장할 경로 얻기

     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

     NSString *documentsDirectory = [paths objectAtIndex:0];

     NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:@"latest_photo.png"];

    

    

    NSLog(@"paths  %@" , paths);

    // /var/mobile/Containers/Data/Application/0BDB0C7B-5FC7-4F47-A46F-4EF2FB521EE5/Documents

    NSLog(@"documentsDirectory %@" , documentsDirectory);

    //    var/mobile/Containers/Data/Application/0BDB0C7B-5FC7-4F47-A46F-4EF2FB521EE5/Documents

    NSLog(@"imagePath %@"  , imagePath);

    //  var/mobile/Containers/Data/Application/0BDB0C7B-5FC7-4F47-A46F-4EF2FB521EE5/Documents/latest_photo.png

    

     //피커에서 이미지 선택 및 저장

     NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];

     NSLog(@"mediaType %@"  , mediaType); //public.image

    

    //파일에 저장하기

     if ([mediaType isEqualToString:@"public.image"]){

//          UIImage *pickedImage2 = info[UIImagePickerControllerEditedImage];

//         NSData *webData = UIImagePNGRepresentation(pickedImage2);

//         [webData writeToFile:imagePath atomically:YES];

         

         NSLog(@"저장되었습니다.");

          [self dismissViewControllerAnimated:YES completion:nil];

     }else{

         NSLog(@"저장실패");

     }

}



//취소

-(void) imagePickerControllerDidCancel:(UIImagePickerController *)picker{

    

    // handle cancel

    NSLog(@"imagePickerControllerDidCancel 호출");

    [self dismissViewControllerAnimated:YES completion:nil];

}


@end



//path is /private/var/containers/Shared/SystemGroup/

//파일디렉토리에 저장하기

//https://stackoverflow.com/questions/4957972/iphone-uiimagepickercontroller-save-the-image-to-app-folder


예제 파일 다운로드

무제 폴더.zip



objective c nsoperation 예제

 개념정리

 

 #.

 NSOperation =.  하나의 작업 단위.

 NSOperation 모델링 상태, 우선순위, 의존성, 관리를 지원하는 추상 클래스이다.

 NSOperation 상속하여 커스텀 클래스를 만들어 활용함.

 

 #.

 네트워크 요청, 이미지 리사이즈, 텍스트 처리, 또는 기타 다양한 반복처리등을 처리하는 NSOperation 있을때,

  특정 작업이 담겨있는 NSOperation 객체는 혼자서 일을 없다.

 이러한 작업을 진행해주는 녀석이 NSOperationQueue이다.

 

 #.

 NSOperationQueue : NSOperationQueue 작업의 동시 실행을 조절

 기본적으로는 First-In-First-Out으로 동작

 maxConcurrentOperationCount 프로퍼티로 특정시간 동안 동시에 실행 가능한 작업의 숫자를 정함.

 

 

 - 스레드를 이용하여 병렬처리하기 3가지 방법

 1. GCD

 2. NSThread

 3. NSOperationQueue

 


 -NSOperationQueue 이용해서 병렬처리 하기 순서

 

1.새로운 NSOperationQueue 작성

2.작성한 operationQueue 병렬로 실행하고 싶은 처리(operation) 넣기



 1.작업간 의존성 부여해서 특정 작업이 끝난 수행할 특정 작업 추가  : addDependency

   제거 : removeDependency

 

 )

 MyLengthOperation *mylengthOperation = [[MyLengthOperation alloc]init];

 MyCalcOperation *calcOperation = [[MyCalcOperation alloc]init];

 [mylengthOperation addDependency:calcOperation];

 

 

 2. setCompletionBlock 메소드로 block 등록하면 작업이 완료된 후에 block 수행

 

 )

 MyLengthOperation *mylengthOperation = [[MyLengthOperation alloc]init];

 [mylengthOperation setCompletionBlock:^{

     NSLog(@"Complete");

     }] ;

 [mylengthOperation start];

 

 

 3. UI작업은 mainThread에서 처리

 

 )

     NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];

         [mainQueue addOperationWithBlock:^{

         // UI 갱신은 메인쓰레드에서!

         [self.tableView reloadData];

         [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];

         }];

 


 예제



 


//

//  ViewController.m

//  NSOperationQ

//

//  Created by 위피아 on 26/11/2018.

//  Copyright © 2018 위피아. All rights reserved.

//


#import "ViewController.h"

#import "MyLengthOperation.h"

#import "MyCalcOperation.h"


@interface ViewController (){

@private

NSOperationQueue    *queue;

}

@end


@implementation ViewController


- (void)viewDidLoad {

    [super viewDidLoad];

   

    MyLengthOperation *mylengthOperation = [[MyLengthOperation alloc]init];

    mylengthOperation.queuePriority = NSOperationQueuePriorityHigh;

    

    

     MyCalcOperation *calcOperation = [[MyCalcOperation alloc]init];

     calcOperation.queuePriority = NSOperationQueuePriorityHigh;

    


    if(queue == nil){

        queue = [[NSOperationQueue alloc]init];

        [queue setMaxConcurrentOperationCount:5];

         NSLog(@" !NSOperationQueue가 없어서 생성했습니다!!!!!");

    }else{

         NSLog(@"  NSOperationQueue가 있어요!!!!!");

    }

    

    [queue addOperation:mylengthOperation];

    [queue addOperation:calcOperation];

    

    

     //[mylengthOperation addDependency:calcOperation];

     //[mylengthOperation cancel];


}



- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}



@end





//

//  MyLengthOperation.m

//  NSOperationQ

//

//  Created by 위피아 on 26/11/2018.

//  Copyright © 2018 위피아. All rights reserved.

//


#import "MyLengthOperation.h"


@implementation MyLengthOperation


- (void)main

{

    

    NSLog(@"MyLengthOperation  #############################");

    for (int i = 0; i < 1000; i++) {

        if ( self.isCancelled ) {

            break;

        }

        NSLog(@"%f  #####################", sqrt(i));

    }

}



@end


//

//  MyCalcOperation.m

//  NSOperationQ

//

//  Created by 위피아 on 26/11/2018.

//  Copyright © 2018 위피아. All rights reserved.

//


#import "MyCalcOperation.h"


@implementation MyCalcOperation



- (void)main

{

    

    NSLog(@"MyCalcOperation ***************************");

    for (int i = 0; i < 1000; i++) {

        if ( self.isCancelled ) {

            break;

        }

        NSLog(@"%f   ***************************", sqrt(i));

    }

}



@end




  1. 2018.11.27 17:12

    비밀댓글입니다

ios objective c - http 네트워크 통신 1


이번포스팅에서는 ios 네트워크 통신에 대해서 간단히 정리한다. 

상황

클라이언트: ios 폰

중간에서 컨트롤하는 녀석: 퍼사드

서버: json 데이터형식


ios 네트워크에서 퍼사드 디자인 패턴을 알아본다. 퍼사드란 클라이언트와 서버 사이에 위치하는 녀석으로 클라이언트(앱)의 수정과 배포를 획기적으로 수월하게 해주는 녀석이다. 아래 코드는 날씨어플 버젼 1에서 버젼2로 코드수정을 했고, 주식어플 버젼1에서 버젼2로 코드 수정을하고 업데이트를 했다. 퍼사드 패턴을 써서 얼마나 쉽고 간결하게 처리를 해줬는지 확인했으면 ok. 코드에 주석을 달아 놓았다.


네비게이션


appdelegate.h

//

//  AppDelegate.h

//  1NetWork

//

//  Created by service on 2018. 11. 13..

//  Copyright © 2018년 service. All rights reserved.

//


#import <UIKit/UIKit.h>


//탭바 컨트롤러 델리게이트 추가해주기

@interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>


//윈도우 뷰

@property (strong, nonatomic) UIWindow *window;


//탭바

@property (strong, nonatomic) UITabBarController *tabBarController;


//버젼별 url을 할당받을 객체

@property (strong, nonatomic) NSURL                    *urlForWeatherVersion1;

@property (strong, nonatomic) NSURL                    *urlForWeatherVersion2;

@property (strong, nonatomic) NSURL                    *urlForStockVersion1;

@property (strong, nonatomic) NSURL                    *urlForStockVersion2;


@end



appdelegate.m

//

//  AppDelegate.m

//  1NetWork

//

//  Created by service on 2018. 11. 13..

//  Copyright © 2018년 service. All rights reserved.

//


#import "AppDelegate.h"

#import "WeatherViewController.h"

#import "StockViewController.h"


@interface AppDelegate ()

@end


@implementation AppDelegate


@synthesize window = _window;

@synthesize tabBarController = _tabBarController;

@synthesize urlForStockVersion1 = _urlForStockVersion1;

@synthesize urlForStockVersion2 = _urlForStockVersion2;

@synthesize urlForWeatherVersion1 = _urlForWeatherVersion1;

@synthesize urlForWeatherVersion2 = _urlForWeatherVersion2;


//앱이 실행될때 초기화 작업해주기!

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    //윈도우 초기화 해주기

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    

    //테이블뷰컨트롤러 초기화 해서 네비게이션 컨트롤러에 넣어주기

    UIViewController *weatherVC = [[UINavigationController alloc] initWithRootViewController:[[WeatherViewController alloc]init]];

    UIViewController *stockVC = [[UINavigationController alloc] initWithRootViewController:[[StockViewController alloc]init]];

    

    //탭바를 초기화 하고 탭바에 위에서 만든 두가지 컨트롤러를 넣어주었다.

    self.tabBarController = [[UITabBarController alloc]init];

    self.tabBarController.viewControllers = [NSArray arrayWithObjects:weatherVC,stockVC, nil];

    

    //윈도우의 최상위 뷰는 탭바다.

    self.window.rootViewController = self.tabBarController;

    

    //윈도우를 보여줘라.

    [self.window makeKeyAndVisible];

    

    NSLog(@"didFinishLaunchingWithOptions 호출되었습니다.");

    

    

    //인디케이터 로직 호출 해야됨 ~~!

    

    [self loadServiceLocator];

    return YES;

}


//서비스 로케이터 호출

-(void)loadServiceLocator {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        NSError *error = nil;

        

        //url을 넣으면 해당 데이터를 받는다

        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://api.myjson.com/bins/18ckhy"]

                                             options:NSDataReadingUncached

                                               error:&error];

        //성공

        if (error == nil) {

            //json 데이터 파싱해서 dictionary에 할당.

            NSDictionary *locationDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];

            

                if (error == nil){

                    //findURLForServiceNamed() 메서드에 각각의 파라미터를 넣으면 해당 url을 리턴해준다 : 위에서 받은 locationDictionary 데이터 넣어줌.

                    self.urlForWeatherVersion1 = [self findURLForServiceNamed:@"weather" versoin:1 inDictionary:locationDictionary];

                    self.urlForWeatherVersion2 = [self findURLForServiceNamed:@"weather" versoin:2 inDictionary:locationDictionary];

                    self.urlForStockVersion1 = [self findURLForServiceNamed:@"stockQuote" versoin:1 inDictionary:locationDictionary];

                    self.urlForStockVersion2 = [self findURLForServiceNamed:@"stockQuote" versoin:2 inDictionary:locationDictionary];


                }else{

                    //에러발생:url연결 되었지만 파싱은 안될때

                        NSLog(@" service locator를 파싱할 수 없습니다!. error: %@", error);

                        dispatch_async(dispatch_get_main_queue(), ^{

                            [[[UIAlertView alloc] initWithTitle:@"error"

                                                        message:@"서비스 로케이터를 파싱할수 없습니다."

                                                       delegate:nil

                                              cancelButtonTitle:@"ok"

                                              otherButtonTitles:nil]show];

                        });

                }

            

        }else{

            //url연결 되지 않을때

            NSLog(@"서비스 로케이터를 로드할 수 없습니다. error: %@", error);

            dispatch_async(dispatch_get_main_queue(), ^{

                [[[UIAlertView alloc] initWithTitle:@"Error"

                                            message:@"서비스 로케이터를 로드할 수 없습니다.  url 업데이트해야 되는거 아닌가요?"

                                           delegate:nil

                                  cancelButtonTitle:@"OK"

                                  otherButtonTitles:nil] show];

            });

        }

    });

}



-(NSURL*) findURLForServiceNamed:(NSString*)serviceName versoin:(NSInteger)versionNumber inDictionary:(NSDictionary*)locatorDictionary{

    //json형식의 파일에서 배열형식의 데이터를 읽어 온다.

    NSArray *service = [locatorDictionary objectForKey:@"services"];

    

    //딕셔너리 형식의 데이터 추출

    for (NSDictionary *serviceInfo in service) {

        NSString *name = [serviceInfo objectForKey:@"name"];

        NSInteger version = [[serviceInfo objectForKey:@"version"] intValue];

        

        

        //추출된 데이터와 파라미터로 넘어온 이름, 버젼을 비교해서 같으면 해당 url을 넘겨준다.

        if ([name caseInsensitiveCompare:serviceName] == NSOrderedSame && version == versionNumber) {

            return [NSURL URLWithString:[serviceInfo objectForKey:@"url"]];

        }

    }

    

    return nil;

}



- (void)applicationDidBecomeActive:(UIApplication *)application {

    //서비스 로케이터 로드

     NSLog(@"applicationDidBecomeActive 호출되었습니다.");

    [self loadServiceLocator];

}



- (void)applicationWillResignActive:(UIApplication *)application {

    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.

    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.

}



- (void)applicationDidEnterBackground:(UIApplication *)application {

    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.

    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

}



- (void)applicationWillEnterForeground:(UIApplication *)application {

    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.

}





- (void)applicationWillTerminate:(UIApplication *)application {

    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

}



@end


weathercontroller.h

//

//  WeatherViewController.h

//  1NetWork

//

//  Created by service on 2018. 11. 13..

//  Copyright © 2018년 service. All rights reserved.

//


#import <UIKit/UIKit.h>


@interface WeatherViewController : UITableViewController

@end


weathercontroller.m

//

//  WeatherViewController.m

//  1NetWork

//

//  Created by service on 2018. 11. 13..

//  Copyright © 2018년 service. All rights reserved.

//


#import "WeatherViewController.h"

#import "AppDelegate.h"


@interface WeatherViewController (){

    UISegmentedControl      *versionSelector;

    NSString                *v1_city;

    NSString                *v1_state;

    NSString                *v1_temperature;

    

    NSString            *v2_city;

    NSString            *v2_state;

    NSInteger            v2_temperature;

    NSString            *v2_conditions;

    

}


-(void)versionChanged:(id)sender;

-(void)loadVersion1Weather;

@end




@implementation WeatherViewController


//초기화 작업

-(id)init{

    self = [super initWithStyle:UITableViewStyleGrouped];

    if(self){

        //탭바의 아이템과 이미지를 초기화 하고 넘겨준다.

        self.tabBarItem.title = NSLocalizedString(@"Weather", @"Weather");

        self.tabBarItem.image = [UIImage imageNamed:@"weather"];

    }

    return self;

}



- (void)viewDidLoad {

    [super viewDidLoad];

    

    NSLog(@"WeatherViewController 호출되었습니다.");

    

    //version selector 설정 : (제일 상단에 네비게이션 바를 셋팅한다.)

    versionSelector = [[UISegmentedControl alloc] initWithItems:

                       [NSArray arrayWithObjects:

                        NSLocalizedString(@"Version 1", @"Version 1"),

                        NSLocalizedString(@"Veresion 2", @"Version 2"),nil]];

    versionSelector.segmentedControlStyle = UISegmentedControlStyleBar;

    versionSelector.selectedSegmentIndex = 0;

    

    [versionSelector addTarget:self

                        action:@selector(versionChanged:)

              forControlEvents:UIControlEventValueChanged];

    self.navigationItem.titleView = versionSelector;

}



//버젼 변경

-(void)versionChanged:(id)sender{

    [self.tableView reloadData];

}


//weather1 데이터 로드

-(void)loadVersion1Weather{

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];

        

        if (appDelegate.urlForStockVersion1 != nil) {

            NSError *error = nil;

            //appDelegate의 urlForWeatherVersion1 변수가 해당 url을 가지고 있음.

            //넣어주고 데이터를 받는다.

            NSData *data = [NSData dataWithContentsOfURL:appDelegate.urlForWeatherVersion1

                                                 options:NSDataReadingUncached

                                                   error:&error];

            if (error == nil) {

                //딕셔너리에 넣어준다.

                NSDictionary *weatherDictionary = [NSJSONSerialization JSONObjectWithData:data

                                                                                  options:NSJSONReadingMutableLeaves

                                                                                    error:&error];

                if (error == nil) {

                    //딕셔너리에서 키값을 이용해서 데이터를 하나씩 추출한다.

                    v1_city = [weatherDictionary objectForKey:@"city"];

                    v1_state = [weatherDictionary objectForKey:@"state"];

                    v1_temperature = [weatherDictionary objectForKey:@"currentTemperature"];

                    

                    //update the table on the ui thread

                    dispatch_async(dispatch_get_main_queue(), ^{

                        //테이블을 리로드 해준다!

                        [self.tableView reloadData];

                    });

                    

                }else{

                    [self showParseError];

                }

            } else{

                [self showLoadError];

            }

        }else{

             [self showLoadError];

        }

        

    });

}


- (void)loadVersion2Weather {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];

        

        if (appDelegate.urlForWeatherVersion2 != nil) {

            NSError *error = nil;

            NSData *data = [NSData dataWithContentsOfURL:appDelegate.urlForWeatherVersion2

                                                 options:NSDataReadingUncached

                                                   error:&error];

            

            if (error == nil) {

                NSDictionary *weatherDictionary = [NSJSONSerialization JSONObjectWithData:data

                                                                                  options:NSJSONReadingMutableLeaves

                                                                                    error:&error];

                

                if (error == nil) {

                    v2_city = [weatherDictionary objectForKey:@"city"];

                    v2_state = [weatherDictionary objectForKey:@"state"];

                    v2_temperature = [[weatherDictionary objectForKey:@"currentTemperature"] intValue];

                    v2_conditions = [weatherDictionary objectForKey:@"currentConditions"];

                    

                    // update the table on the UI thread

                    dispatch_async(dispatch_get_main_queue(), ^{

                        [self.tableView reloadData];

                    });

                    

                } else {

                    NSLog(@"Unable to parse weather because of error: %@", error);

                    [self showParseError];

                }

                

            } else {

                [self showLoadError];

            }

            

        } else {

            [self showLoadError];

        }

    });

}


//로드할때 에러발생

-(void)showLoadError

{

    dispatch_async(dispatch_get_main_queue(), ^{

       [[[UIAlertView alloc] initWithTitle:@"error"

                                   message:@"unable to load weather data"

                                  delegate:nil

                         cancelButtonTitle:@"ok"

                         otherButtonTitles:nil] show];

        

    });

}


//파싱할때 에러 발생

-(void)showParseError

{

    dispatch_async(dispatch_get_main_queue(), ^{

        [[[UIAlertView alloc] initWithTitle:@"Error"

                                    message:@"Unable to parse weather data."

                                   delegate:nil

                          cancelButtonTitle:@"OK"

                          otherButtonTitles:nil] show];

    });

}



- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


#pragma mark - Table view data source


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

#warning Incomplete implementation, return the number of sections

    return 2;

}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

#warning Incomplete implementation, return the number of rows

    

    //색션이 0일때

    if (section == 0) {

        //세그먼트가 0이면 행의 갯수는 3

        if (versionSelector.selectedSegmentIndex == 0) {

            return 3;

        } else if(versionSelector.selectedSegmentIndex == 1) {

            return 4;

        }

        

        //색션이 2일때 refresh button

    } else if(section == 1){

        return 1;

    }

    

    return 0;

}



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = nil;

    

    //첫번째 섹션일때

    if(indexPath.section == 0){

        static NSString *infoCellId = @"infoCell";

        cell = [tableView dequeueReusableCellWithIdentifier:infoCellId];

        

        if (cell == nil) {

            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:infoCellId];

            cell.selectionStyle = UITableViewCellSelectionStyleNone;

        }

        

        //첫번째 섹션에 첫번째 세그먼트 : version1

        if(versionSelector.selectedSegmentIndex == 0){

            if (indexPath.row == 0) {

                cell.textLabel.text = NSLocalizedString(@"City", @"City");

                cell.detailTextLabel.text = v1_city;

            } else if(indexPath.row == 1){

                cell.textLabel.text = NSLocalizedString(@"State", @"State");

                cell.detailTextLabel.text = v1_state;

            }else if(indexPath.row == 2){

                cell.textLabel.text = NSLocalizedString(@"Temperature", @"Temperature");

                cell.detailTextLabel.text = [v1_temperature stringByAppendingString:@"\u00B0F"];

            }

        }

        //첫번째 섹션에 두번째 세그번트 : version2

     else if(versionSelector.selectedSegmentIndex == 1){

        

            // city

        if (indexPath.row == 0) {

            cell.textLabel.text = NSLocalizedString(@"City", @"City");

            cell.detailTextLabel.text = v2_city;

            

            // state

        } else if (indexPath.row == 1) {

            cell.textLabel.text = NSLocalizedString(@"State", @"State");

            cell.detailTextLabel.text = v2_state;

            

            // temperature

        } else if (indexPath.row == 2) {

            cell.textLabel.text = NSLocalizedString(@"Temperature", @"Temperature");

            cell.detailTextLabel.text = [NSString stringWithFormat:@"%i\u00B0F", v2_temperature];

            

            // conditions

        } else if (indexPath.row == 3) {

            cell.textLabel.text = NSLocalizedString(@"Conditions", @"Conditions");

            cell.detailTextLabel.text = v2_conditions;

        }

        

     }

        

        //두번째 섹션

    }else if(indexPath.section == 1){

        static NSString *buttonCellId = @"buttonCell";

        cell = [tableView dequeueReusableCellWithIdentifier:buttonCellId];

        if (cell == nil) {

            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:buttonCellId];

        }

        cell.textLabel.text = NSLocalizedString(@"Refresh Richmond, VA", @"Refresh Richmond, VA");

        cell.textLabel.textAlignment = UITextAlignmentCenter;

        cell.textLabel.textColor = [UIColor blueColor];

    }

    

    return cell;

}


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

    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    

    if (indexPath.section == 1) {

        

        //version1

        if (versionSelector.selectedSegmentIndex == 0) {

        

            [self loadVersion1Weather];

        //version2

        }else if(versionSelector.selectedSegmentIndex == 1){

            [self loadVersion2Weather];

        }

    }

}


@end


stock.h

//

//  StockViewController.h

//  1NetWork

//

//  Created by service on 2018. 11. 13..

//  Copyright © 2018년 service. All rights reserved.

//


#import <UIKit/UIKit.h>


@interface StockViewController : UITableViewController

@end



stock.m

//

//  StockViewController.m

//  1NetWork

//

//  Created by service on 2018. 11. 13..

//  Copyright © 2018년 service. All rights reserved.

//


#import "StockViewController.h"

#import "AppDelegate.h"

@interface StockViewController (){

    UISegmentedControl *versionSelector;

    NSString            *v1_symbol;

    NSString            *v1_name;

    NSNumber            *v1_currentPrice;

    

    NSString            *v2_symbol;

    NSString            *v2_name;

    NSNumber            *v2_currentPrice;

    NSNumber            *v2_openingPrice;

    NSString            *v2_percentageChange;

}

- (void)versionChanged:(id)sender;

- (void)loadVersion1Stock;

- (void)loadVersion2Stock;

@end


@implementation StockViewController



-(id)init{

    self = [super initWithStyle:UITableViewStyleGrouped];

    if(self){

        //탭바의 아이템과 이미지를 초기화 하고 넘겨준다.

        self.tabBarItem.title = NSLocalizedString(@"Stock Quote", @"Stock Quote");

        self.tabBarItem.image = [UIImage imageNamed:@"stock"];

        

        //변수 초기화

        v1_symbol = @"";

        v1_name = @"";

        v1_currentPrice = nil;

        

        v2_symbol = @"";

        v2_name = @"";

        v2_currentPrice = nil;

        v2_openingPrice = nil;

        v2_percentageChange = @"";

        

    }

    return self;

}


- (void)viewDidLoad {

    [super viewDidLoad];

    NSLog(@"StockViewController 호출되었습니다.");

    // Uncomment the following line to preserve selection between presentations.

    // self.clearsSelectionOnViewWillAppear = NO;

    

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.

    // self.navigationItem.rightBarButtonItem = self.editButtonItem;

}


- (void)versionChanged:(id)sender {

    [self.tableView reloadData];

}


- (void)loadVersion1Stock {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

       AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];

        if (appDelegate.urlForStockVersion1 != nil) {

            NSError *error = nil;

            NSData *data = [NSData dataWithContentsOfURL:appDelegate.urlForStockVersion1

                                                 options:NSDataReadingUncached

                                                   error:&error];

            

            if (error == nil) {

                NSDictionary *stockDictionary = [NSJSONSerialization JSONObjectWithData:data

                                                                                options:NSJSONReadingMutableLeaves

                                                                                  error:&error];

                

                if (error == nil) {

                    v1_symbol = [stockDictionary objectForKey:@"symbol"];

                    v1_name = [stockDictionary objectForKey:@"name"];

                    v1_currentPrice = [NSNumber numberWithFloat:[[stockDictionary objectForKey:@"currentPrice"] floatValue]];

                    

                    // update the table on the UI thread

                    dispatch_async(dispatch_get_main_queue(), ^{

                        [self.tableView reloadData];

                    });

                    

                } else {

                    NSLog(@"Unable to parse stock quote because of error: %@", error);

                    [self showParseError];

                }

                

            } else {

                [self showLoadError];

            }

            

        } else {

            [self showLoadError];

        }

        

    

    });

}


- (void)loadVersion2Stock {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];


        if (appDelegate.urlForStockVersion2 != nil) {

            NSError *error = nil;

            NSData *data = [NSData dataWithContentsOfURL:appDelegate.urlForStockVersion2

                                                 options:NSDataReadingUncached

                                                   error:&error];


            if (error == nil) {

                NSDictionary *stockDictionary = [NSJSONSerialization JSONObjectWithData:data

                                                                                options:NSJSONReadingMutableLeaves

                                                                                  error:&error];


                if (error == nil) {

                    v2_symbol = [stockDictionary objectForKey:@"symbol"];

                    v2_name = [stockDictionary objectForKey:@"name"];

                    v2_openingPrice = [stockDictionary objectForKey:@"openingPrice"];

                    v2_currentPrice = [stockDictionary objectForKey:@"currentPrice"];

                    v2_percentageChange = [stockDictionary objectForKey:@"percentageChange"];


                    // update the table on the UI thread

                    dispatch_async(dispatch_get_main_queue(), ^{

                        [self.tableView reloadData];

                    });


                } else {

                    NSLog(@"Unable to parse stock quote because of error: %@", error);

                    [self showParseError];

                }


            } else {

                [self showLoadError];

            }


        } else {

            [self showLoadError];

        }

    });

}

- (void)showLoadError {

    // inform the user on the UI thread

    dispatch_async(dispatch_get_main_queue(), ^{

        [[[UIAlertView alloc] initWithTitle:@"Error"

                                    message:@"Unable to load stock data."

                                   delegate:nil

                          cancelButtonTitle:@"OK"

                          otherButtonTitles:nil] show];

    });

}


- (void)showParseError {

    // inform the user on the UI thread

    dispatch_async(dispatch_get_main_queue(), ^{

        [[[UIAlertView alloc] initWithTitle:@"Error"

                                    message:@"Unable to parse stock data."

                                   delegate:nil

                          cancelButtonTitle:@"OK"

                          otherButtonTitles:nil] show];

    });

}


- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


#pragma mark - Table view data source


//색션 개수

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {


    return 2;

}



- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {


    // stock info

    if (section == 0) {

        

        // version 1 has 3 cells of information

        if (versionSelector.selectedSegmentIndex == 0) {

            return 3;

            

            // version 2 has 5 cells of information

        } else if (versionSelector.selectedSegmentIndex == 1) {

            return 5;

        }

        

        // refresh button

    } else if (section == 1) {

        return 1;

    }

    return 0;

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = nil;

    NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];

    currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;

    

    // info group

    if (indexPath.section == 0) {

        static NSString *infoCellId = @"infoCell";

        cell = [tableView dequeueReusableCellWithIdentifier:infoCellId];

        

        if (cell == nil) {

            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:infoCellId];

            cell.selectionStyle = UITableViewCellSelectionStyleNone;

        }

        

        // version 1

        if (versionSelector.selectedSegmentIndex == 0) {

            

            // symbol

            if (indexPath.row == 0) {

                cell.textLabel.text = @"Symbol";

                cell.detailTextLabel.text = v1_symbol;

                

                // name

            } else if (indexPath.row == 1) {

                cell.textLabel.text = @"Name";

                cell.detailTextLabel.text = v1_name;

                

                // price

            } else if (indexPath.row == 2) {

                cell.textLabel.text = @"Price";

                cell.detailTextLabel.text = [currencyFormatter stringFromNumber:v1_currentPrice];

            }

            

            

            // version 2

        } else if (versionSelector.selectedSegmentIndex == 1) {

            

            // symbol

            if (indexPath.row == 0) {

                cell.textLabel.text = @"Symbol";

                cell.detailTextLabel.text = v2_symbol;

                

                // name

            } else if (indexPath.row == 1) {

                cell.textLabel.text = @"Name";

                cell.detailTextLabel.text = v2_name;

                

                // opening price

            } else if (indexPath.row == 2) {

                cell.textLabel.text = @"Opening Price";

                cell.detailTextLabel.text = [currencyFormatter stringFromNumber:v2_openingPrice];

                

                // current price

            } else if (indexPath.row == 3) {

                cell.textLabel.text = @"Current Price";

                cell.detailTextLabel.text = [currencyFormatter stringFromNumber:v2_currentPrice];

                

                // percentage change

            } else if (indexPath.row == 4) {

                cell.textLabel.text = @"Percent Change";

                cell.detailTextLabel.text = v2_percentageChange;

            }

        }

        

        // button group

    } else if (indexPath.section == 1) {

        static NSString *buttonCellId = @"buttonCell";

        cell = [tableView dequeueReusableCellWithIdentifier:buttonCellId];

        

        if (cell == nil) {

            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:buttonCellId];

        }

        

        cell.textLabel.text = @"Refresh AAPL";

        cell.textLabel.textAlignment = UITextAlignmentCenter;

        cell.textLabel.textColor = [UIColor blueColor];

    }

    

    return cell;

}



#pragma mark - UITableViewDelegate


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

    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    

    if (indexPath.section == 1) {

        

        // version 1

        if (versionSelector.selectedSegmentIndex == 0) {

            [self loadVersion1Stock];

            

            // version 2

        } else if (versionSelector.selectedSegmentIndex == 1) {

            [self loadVersion2Stock];

        }

    }

}


@end


중간에서 조정해주는 퍼사드! (url은 직접 로컬에서 서버를 돌려서 실행해본다.)


{

  "services": [

    {

      "name": "stockQuote",

      "url": "https://api.myjson.com/bins/u25za",

      "version": 1

    },

    {

      "name": "stockQuote",

      "url": "https://api.myjson.com/bins/1cilqe",

      "version": 2

    },

    {

      "name": "weather",

      "url": "https://api.myjson.com/bins/pap52",

      "version": 1

    },

    {

      "name": "weather",

      "url": "https://api.myjson.com/bins/pw4qu",

      "version": 2

    }

  ]

}



날씨 버젼1 데이터

{

  "city": "Richmond",

  "state": "Virginia",

  "currentTemperature": "63"

}

날씨 버젼2 데이터

{

  "city": "Richmond",

  "state": "Virginia",

  "currentTemperature": 63,

  "currentConditions": "Mostly Cloudy"

}

주식 버젼1 데이터

{

  "symbol": "AAPL",

  "name": "Apple Inc.",

  "currentPrice": "-2.92%"

}

주식 버젼2 데이터

{

  "symbol": "AAPL",

  "name": "Apple Inc.",

  "openingPrice": 545.31,

  "currentPrice": 530.12,

  "percentageChange": "-2.92%"

}


잘돌아가는지 확인! 










ios objective c 코어오디오 다루기 6 - 오디오 파일 변환


//

//  main.m

//  6

//

//  Created by service on 2018. 11. 12..

//  Copyright © 2018년 service. All rights reserved.

//


/*

 - mp3 나 aac 와 같은 엔코드된 파일을 .caf 컨테이너 파일로 변환하기

 - 하나의 파일에서 데이터를 읽고, 메모리의 일부 패킷을 변환하고, 결과를 다른 파일에 쓰기

 - 오디오 파일 서비스 사용함.

 

 */


#import <Foundation/Foundation.h>

#import <AudioToolbox/AudioToolbox.h>

#import <CoreServices/CoreServices.h>

#define kInputFileLocation    CFSTR("/Users/service/Desktop/test_cbr.mp3")


typedef struct MyAudioConverterSettings

{

    AudioStreamBasicDescription       inputFormat;                  //입력 스트림의 데이터 형식

    AudioStreamBasicDescription       outputFormat;                 //출력 스트림의 데이터 형식

    

    AudioFileID                     inputFile;                      //입력 파일의 참조를 추적함

    AudioFileID                     outputFile;                     //출력 파일의 참조를 추적함

    

    UInt64                          inputFilePacketIndex;           //읽고 있는 현재 패킷의 수

    UInt64                          inputFilePacketCount;           //입력 파일의 전체 패킷 수

    UInt32                          inputFilePacketMaxSize;         //입력 패킷의 최대 크기

    AudioStreamPacketDescription    *inputFilePacketDescription;      //데이터를 읽을 때마다 입력 파일에서 읽어들이는 배열의 포인터

    void *sourceBuffer;

    

} MyAudioConverterSettings;


OSStatus MyAudioConverterCallback

(

    AudioConverterRef inAudioConverter,

    UInt32 *ioDataPacketCount,

    AudioBufferList *ioData,

    AudioStreamPacketDescription **outDataPacketDescription,

 void *inUserdata

);


void Convert(MyAudioConverterSettings *mySetting);








//에러 처리기 : 리턴값이 0이 아니면 에러메시지를 출력하고, 프로그램 종료

static void CheckResult(OSStatus result, const char *operation)

{

    if (result == noErr) {

        return;

    }

    

    char errorString[20];

    *(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(result);

    if (isprint(errorString[1]) && isprint(errorString[2]) && isprint(errorString[3]) && isprint(errorString[4])) {

        errorString[0] = errorString[5] = '\'';

        errorString[6] = '\0';

    } else

        // 포멧이 정수 값일때

        sprintf(errorString, "%d", (int)result);

    

    fprintf(stderr, "에러발생: Error: %s (%s)\n", operation, errorString);

    

    exit(1);

    

    

}

#pragma mark - audio converter -



OSStatus MyAudioConverterCallback(AudioConverterRef inAudioConverter,

                                  UInt32 *ioDataPacketCount,

                                  AudioBufferList *ioData,

                                  AudioStreamPacketDescription **outDataPacketDescription,

                                  void *inUserData){


    //사용자 데이터 객체를 myaudioConverterSettings 구조체로 형변환

    MyAudioConverterSettings *audioConverterSettings = (MyAudioConverterSettings *)inUserData;

    

    //실패했을 때 초기화. 오디오 버퍼를 0으로 초기화 할 수 있다

    ioData -> mBuffers[0].mData = NULL;

    ioData -> mBuffers[0].mDataByteSize = 0;

    

    

    

    //얼마나 많은 패킷을 읽을 지 계산

    //ioDataPacketCount는 최초에 Convert(0)에서 계산한 packetPerBuffer이지만, 입력의 마지막에 도달할 때 이것보다 적은 패킷이 남아 있다.

    //남아 있는 패킷의 수로 ioDataPacketCount를 재설정한다.

    //입력파일에서 읽을 수 있는 패킷의 수 결정

    //무엇이 남았는지 읽는다.

    

    if (audioConverterSettings->inputFilePacketIndex + *ioDataPacketCount > audioConverterSettings-> inputFilePacketCount) {

        *ioDataPacketCount = audioConverterSettings->inputFilePacketCount - audioConverterSettings->inputFilePacketIndex;

    }

        

        if(*ioDataPacketCount == 0 ){

            return noErr;

        }

        

        //읽을 데이터가 있는 경우 오디오 데이터를 받기 위한 버퍼를 준비할 필요가 있다.

        //설정 구조체가 가리키는 오래된 버퍼를 free 하고 (오래된 버퍼의 데이터는 이미 소비됐기 때문에)

        //읽으려고 하는 패킷의 수를 저장할 충분히 큰 새로운 버퍼를 할당한다.

        //채우고 변환할 버퍼 할당

        if (audioConverterSettings->sourceBuffer !=NULL) {

            free(audioConverterSettings->sourceBuffer);

            audioConverterSettings->sourceBuffer = NULL;

        }

        

        audioConverterSettings->sourceBuffer = (void *)calloc(1, *ioDataPacketCount * audioConverterSettings->inputFilePacketMaxSize);

        

        //할당된 버퍼를 가지고 소스 파일에서 읽은 패킷을 그 버퍼로 쓸 준비가 됐다.

        UInt32 outByteCount = 0;

        OSStatus result = AudioFileReadPackets(audioConverterSettings->inputFile,

                                               true,

                                               &outByteCount,

                                               audioConverterSettings->inputFilePacketDescription,

                                               audioConverterSettings->inputFilePacketIndex,

                                               ioDataPacketCount,

                                               audioConverterSettings->sourceBuffer);

        

        #ifdef MAC_OS_X_VERSION_10_7

                if (result == kAudioFileEndOfFileError && *ioDataPacketCount) result = noErr;

        #else

                if (result == eofErr && *ioDataPacketCount) result = noErr;

        #endif

                else if (result != noErr) return result;

        

        

    //읽은 것의 결과를 가지고 소스 파일 위치와 audiobuffer 멤버를 업데이트

     audioConverterSettings->inputFilePacketIndex += *ioDataPacketCount;

        

        ioData->mBuffers[0].mData = audioConverterSettings->sourceBuffer;

        ioData->mBuffers[0].mDataByteSize = outByteCount;

        

        if (outDataPacketDescription)

            *outDataPacketDescription = audioConverterSettings->inputFilePacketDescription;

        

        return result;

    

    

}



//최종적으로 audioFileWritePackets()로 전달될 수 있는 패킷 버퍼를 채우는 audioconverterfillcomplexbuffer()를 반복적으로 호출하는 동작을 한다.

//오디오 변환기 생성

void Convert(MyAudioConverterSettings *mySettings){

    

    AudioConverterRef audioConverter;

    CheckResult(

                AudioConverterNew(&mySettings ->inputFormat, &mySettings ->outputFormat, &audioConverter),

                "audioConverterNew failed");

    

    //패킷정보 배열을 얼마나 크게 할당해야 하는지 계산

    //형식이 가변비트율인지 여부, 버퍼가 적어도 하나의 패킷을 저장할 만큼 충분히 큰지 등이 해당한다.

    //패킷 버퍼 배열 크기와 가변 비트율 데이터의 버퍼당 패킷 수를 결정

    

    UInt32 packetsPerBuffer = 0;

    UInt32 outputBufferSize = 32 * 1024;

    UInt32 sizePerpacket = mySettings -> inputFormat.mBytesPerPacket;

    

    if(sizePerpacket == 0)

    {

        UInt32 size = sizeof(sizePerpacket);

        CheckResult(

                    AudioConverterGetProperty(audioConverter, kAudioConverterPropertyMaximumOutputPacketSize, &size, &sizePerpacket),

                    "couldn't get kAudioConverterPropertyMaximumOutputPacketsSize");

    

        // make sure the buffer is large enough to hold at least one packet

        if (sizePerpacket > outputBufferSize) {

            outputBufferSize = sizePerpacket;

            packetsPerBuffer = outputBufferSize / sizePerpacket;

            mySettings -> inputFilePacketDescription = (AudioStreamPacketDescription*)malloc(sizeof(AudioStreamPacketDescription) * packetsPerBuffer);

        }

        

    }else

    {   //참고:

        //가변 비트율 데이터는 mBytesPerPacket == 0을 가진 asbd에 의해서 표시됨

        //이 경우 변환기에서 최대 패킷 크기를 가진다. 그리고 그 값을 기본 버퍼 크기 (32kb)와 비교하고 버퍼에 적어도 하나의 패킷을 저장할 수 있다고 확신하는 둘 중에 큰것을 취한다.

        //그 후에 패킷 정보 배열을 처리해야 한다.

        //버퍼에 얼마나 많은 패킷이 들어갈지 결정할 수 있고, 그만큼의 audioStreamPacketDescriptions를 저장할 수 있는 충분한 공간을 할당한다.

        //고정 비트율 데이터의 버퍼당 패킷 결정하기

        packetsPerBuffer = outputBufferSize / sizePerpacket;

    }

    

    

    

    //오디오 변환 버퍼를 위해 메모리 할당

    UInt8 *outputBuffer = (UInt8 *)malloc(sizeof(UInt8) * outputBufferSize);

    

    //데이터를 변환하고 쓰기위한 반복문

    //출력파일의 어디에 있는지 기억하기 위한 카운터.

    UInt32 outputFilePacketPosition = 0;

    while (1) {

        


        //변환된 데이터를 받기 위해서 AudioBufferList 준비하기

        AudioBufferList convertedData;

        convertedData.mNumberBuffers = 1;

        convertedData.mBuffers[0].mNumberChannels = mySettings->inputFormat.mChannelsPerFrame;

        convertedData.mBuffers[0].mDataByteSize = outputBufferSize;

        convertedData.mBuffers[0].mData = outputBuffer;

     

        

        //수신할 준비가 된 패킷의 수를 전달하는 포인터를 사용한다.

        //반환될때 실제로 버퍼에 있는 변환된 패킷의 수를 얻는다.

        UInt32 ioOutputDataPackets = packetsPerBuffer;

        OSStatus error = AudioConverterFillComplexBuffer(

                                                         audioConverter,

                                                         MyAudioConverterCallback,

                                                         mySettings,                                                                                //콜백을 위한 사용자 데이터 포인터

                                                         &ioOutputDataPackets,                                                                      //패킷수

                                                         &convertedData,                                                                            //변환된 데이터를 수신할 포인터

                                                         (mySettings->inputFilePacketDescription ? mySettings->inputFilePacketDescription : nil));  //패킷정보의 배열을 받을 포인터

        

        if (error || !ioOutputDataPackets)

        {

            break;

        }

        

        //변환된 데이터를 오디오 파일에 작성

        //변환된 데이터를 출력파일에 작성

        CheckResult(

                    AudioFileWritePackets(mySettings->outputFile,                                                       //작성할 파일,

                                          FALSE,                                                                        //캐시설정

                                          ioOutputDataPackets,                                                          //작성할 크기

                                          NULL,                                                                         //패킷정보의배열(pcm출력 파일은 고정비트율,패킷정보를 제공x)

                                          outputFilePacketPosition / mySettings->outputFormat.mBytesPerPacket,          //첫번째 작성하는 패킷의 인덱스

                                          &ioOutputDataPackets,                                                         //쓰여진 패킷의수

                                          convertedData.mBuffers[0].mData),                                             //오디오 변환기에서 받은 오디오 데이터의 버퍼 제공

                    "couldn't write packets to file");

        

        //패킷을 파일에 쓴 이후에 패킷의 수를 각 pcm 패킷에 바이트로 곱함으로써 출력 위치를 업데이트 한다.

        outputFilePacketPosition += (ioOutputDataPackets * mySettings->outputFormat.mBytesPerPacket);

        

    }

    

    AudioConverterDispose(audioConverter);

    free(outputBuffer);

    

    

}



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

   

    //MyAudioConverterSettings을 할당하고 입력파일을 위해 AudioFileID를 열고, 설정 구조체에 AudioFileID를 저장함으로써 시작한다.

    MyAudioConverterSettings audioConverterSetting = {0};

    

    

    //변환을 위한 소스 파일열기

    CFURLRef inputFileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, kInputFileLocation, kCFURLPOSIXPathStyle, false);

    CheckResult(

                AudioFileOpenURL(inputFileURL,

                                 kAudioFileReadPermission,

                                 0,

                                 &audioConverterSetting.inputFile),

                "audioFileOpenURL failed");

    CFRelease(inputFileURL);

    

    //입력 파일이 열린채로 audiostreambascidescription을 얻음으로써 소스 오디오의 형식을 알게된다.

    //kAudioFilePropertyDataFormat을 추출하기 위해서 AudioFileGetProperty을 호출한다.

    //파일에서 부터 오디오 데이터 포멧(asbd) 얻기

    UInt32 propSize = sizeof(audioConverterSetting.inputFormat);

    

    CheckResult(

                AudioFileGetProperty(audioConverterSetting.inputFile,

                                     kAudioFilePropertyDataFormat,

                                     &propSize,

                                     &audioConverterSetting.inputFormat),

                "couldn't get file's data format");

    

    

    

    //소스 파일의 전체 패킷수와 가능한 최대 패킷의 크기 속성을 추출

    //패킷의 수는 얼마나 많은 읽기를 할 것인지 알게 하고, 최대 패킷의 크기는 audiofilereadpackets()에 적절하게 큰 버퍼를 할당한다.

    //입력 오디오 파일에서 패킷 수와 최대 패킷 크기 속성을 얻음

    

    propSize = sizeof(audioConverterSetting.inputFilePacketCount);

    CheckResult(

                AudioFileGetProperty(audioConverterSetting.inputFile,

                                     kAudioFilePropertyAudioDataPacketCount,

                                     &propSize,

                                     &audioConverterSetting.inputFilePacketCount),

                "couldn't gert file's packet count");

    

    propSize = sizeof(audioConverterSetting.inputFilePacketMaxSize);

    CheckResult(

                AudioFileGetProperty(audioConverterSetting.inputFile,

                                     kAudioFilePropertyMaximumPacketSize,

                                     &propSize,

                                     &audioConverterSetting.inputFilePacketMaxSize),

                "couldn't get file's max packet size");

    

    

    //출력 파일을 설정할 준비완료

    //일반적인 lpcm 형식을 설명하기 위해서 asbd 생성

    //16비트 샘플, 두개의 채널(스테레오), 빅엔디언의 샘플이 해당됨

    //파일을 생성하고, audiofileId를 얻기위해서 이 asbd를 audiofilecreateWithURL에 전달한다.

    

    audioConverterSetting.outputFormat.mSampleRate = 44100.0;

    audioConverterSetting.outputFormat.mFormatID = kAudioFormatLinearPCM;

    audioConverterSetting.outputFormat.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;

    

    audioConverterSetting.outputFormat.mBytesPerPacket = 4;

    audioConverterSetting.outputFormat.mFramesPerPacket = 1;

    audioConverterSetting.outputFormat.mBytesPerFrame = 4;

    audioConverterSetting.outputFormat.mChannelsPerFrame = 2;

    audioConverterSetting.outputFormat.mBitsPerChannel = 16;

    

    

    //출력 파일 생성

    CFURLRef outputFileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR("myoutputfile.aif"), kCFURLPOSIXPathStyle, false);

    CheckResult(

                AudioFileCreateWithURL(outputFileURL, kAudioFileAIFFType, &audioConverterSetting.outputFormat, kAudioFileFlags_EraseFile, &audioConverterSetting.outputFile),

                "audiofilecreatewithurl failed");

    

    CFRelease(outputFileURL);

    fprintf(stdout, "converting..\n");

    Convert(&audioConverterSetting);

    

    

    

    

cleanup:

    AudioFileClose(audioConverterSetting.inputFile);

    AudioFileClose(audioConverterSetting.outputFile);

    printf("done\r");

    return 0;

}


/*

 정리

 - main 에서 입력파일을 열었고, 형식과 패킷의 크기속성을 검사했으며, pcm형식을 선택하여 출력 파일을 생성하였다.

 - 패킷을 전송하기 위해 반복적으로 AudioBufferList를 설정하고 압축된 입력 데이터를 PCM으로 변환하기 위해서 AudioConverterFillComplexBuffer()를 호출하는 Converter() 함수를 작성했다.

 - 그리고 AudioFileWritePackets() 로 출력 파일에 작성했다.

 - 변환기의 입력은 개발자가 작성한 콜백 함수에서 오고, 이때 콜백 함수는 임력 파일에서 반복적으로 읽고, AudioBufferList에 전달된 오디오 데이터와 패킷 정보를 제공한다.

 

 */


+ Recent posts

티스토리 툴바