많은경우 여러개의 리턴값을 받기위해 참조를 이용한 값전달 받기를 한다. 이러한 방식은 에러를 전달할 때 가장 많이 사용되므로 아마도 익숙할 것이다. 


보통 이런방식으로..


//output parameter를 사용한 메소드

- (BOOL)huga:(NSString*)s error:(NSError**)outError {


    if (outError) {

        *outError = [NSError errorWithDomain:@"domine" code:-1 userInfo:nil];

        return NO;

    }

    return YES;

}



//사용하기

NSError *error = nil;

BOOL success = [self huga:@"hahaha" error:&error];



그런데 유심히보면 포인터를 두개를 사용한 참조를 이용하는데 왜 그럴까? 그 이유를 내 나름대로 해석하자면 이렇다.

만약 hoge라는 메소드를 만들고 출력 파라메타를 이용한다면 이런식으로 구현할 것이다.


- (void)hoge:(NSString **)outValue {

    *outValue = @"Kjcode!";

}


//값을 줄때 요렇게..

NSString *str = nil;

[obj hoge:&str];




이런식으로 구현된 소스는 컴파일러에서 아래와 같이 해석한다고 한다. 


//complier code.

- (void)hoge:(NSString *__autoreleasing *)value {

    *value = @"Test";




//complier code.

NSString *__strong s = nil;

NSString *__autoreleasing temp = str;

[obj hoge:&temp];

str = temp;




이 이야기는 참조로 넘어가는 값을 __autoreleasing을 해줌으로 인하여 ARC를 적용시켜주는 것이다.

뿐만아니라 시각적으로도 개발자가 포인터에 값을 넣어주어야 하기때문에 반환되는 파라메터라는 것을 금방 눈치챌 수 있을것이다.


만약 포인터 딸랑 하나로 output파라메타를 사용하는 메소드를 만들었다면 문제가 생길소지가 다분하다.

예를 들어 플젝 개발자가 경험이 부족하거나 아니면 경험자라도 메소드안을 꼼꼼히 보지 않는다면 아무생각없이 그 파라메터를 strong프로터피에 값을 전달시킬수도 있을것이다.

그럼 그순간 순환참조가 일어날 것이고 영원히 메모리에서 젖은낙엽처럼 붙어있게 될것이고 짜증나는 야근을 경험할지도 모른다.



휴~ 지금 내가 하는 플젝에서도 출력파라메터를 잔뜩 사용해놓구선 메모리를 잔뜩 잡아먹는 코드를 만들어놔서 고생중인데 물론.. 그 당사자는 이미 그만둔 상태이므로 리팩토링은 남은자의 몫이 되어 버렸다.


암튼 개고생중. (여기서 "개"라는 표현은 욕이 아닌 강조를 위한 최상급 표현임.)



참고사이트

https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html




Posted by 악당잰 트랙백 0 : 댓글 2

댓글을 달아 주세요

  1. addr | edit/del | reply 김기봉 2015.02.03 08:26

    형 이부분
    //complier code.
    NSString *__strong s = nil;
    NSString *__autoreleasing temp = str;
    [obj hoge:&temp];
    str = temp;

    에서
    NSString *__strong s = nil; -> str = nil; 이죠?


제목그대로 네비게이션바에다가 커스텀뷰를 붙이는 방법에는 두가지가 있다.


1. 네비게이션바에서 제공하는 titleView 메소드를 이용하는 방법


UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 120.f, 44.f)];

UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(10.f, 10.f, 100.f, 24.f)];

lbl.backgroundColor = [UIColor whiteColor];

lbl.text = @"custom label~";

lbl.font = [UIFont systemFontOfSize:12.f];

customView.backgroundColor = [UIColor blueColor];

[customView addSubview:lbl];


[self.navigationItem setTitleView:customView];


2. 네비게이션바에다가 직접  addSubview로 붙이는 방법


self.customButton = [UIButton buttonWithType:UIButtonTypeSystem];

    [_customButton setTitle:@"Click!" forState:UIControlStateNormal];

    _customButton.frame = CGRectMake(10.f, 10.f, 50.f, 24.f);

    [_customButton addTarget:self action:@selector(clicked:) forControlEvents:UIControlEventTouchUpInside];

    [self.navigationController.navigationBar addSubview:_customButton];

    

    self.iconImageView = [[UIImageView alloc] initWithFrame:CGRectMake(60.f, 0.f, 40.f, 40.f)];

    _iconImageView.image = [UIImage imageNamed:@"hanjimin"];

    [self.navigationController.navigationBar addSubview:_iconImageView];

    

    

    self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(_iconImageView.frame.origin.x + 40.f,

                                                                10.f, 100.f, 24.f)];

    _titleLabel.text = @"jimin jjang!!";

    [_titleLabel sizeToFit];

    _titleLabel.textColor = [UIColor whiteColor];

    _titleLabel.backgroundColor = [UIColor orangeColor];

    [self.navigationController.navigationBar addSubview:_titleLabel];



위의 두가지 방법 다 유효하다. 다만 붙이는 커스텀된 뷰가 레이아웃이 가변적일때(유저조작에 따라 변경되야 할 경우)는 고민을 해 봐야 한다.


이 문제로 3시간 삽질을 했는데 삽질결과 알아낸 사실은..


- 1.번의 방법으로는 레이아웃 변경자체가 안되고 그냥 가운데로 고정이 되버림.


- 2.번의 방법으로 레이아웃은 조정 가능하지만 만약 커스텀뷰 안에 하위 뷰가 존재할 경우 하위 뷰는 레이아웃 변경이 안된다.(오토레이아웃, 오토리사이즈, 코드상 변경 모두 불가)

 네비게이션에 직접 붙인 뷰(하위1단계레벨)만이 레이아웃 조정가능했다.


버튼이벤트에 레이아웃을 움직여보면.. 잘움직인다.


- (void)clicked:(id)sender {

    

    //update layout!

    _iconImageView.frame = CGRectMake(arc4random()%200 + 60.f,

                                      _iconImageView.frame.origin.y,

                                      _iconImageView.frame.size.width,

                                      _iconImageView.frame.size.height);

    

    _titleLabel.frame = CGRectMake(_iconImageView.frame.origin.x + 40.f,

                                   _titleLabel.frame.origin.y,

                                  _titleLabel.frame.size.width,

                                  _titleLabel.frame.size.height);

    

}





결론은..


네비게이션뷰에다가 복수개의 뷰를 붙일경우엔 편의상 컨테이너뷰를 만들고 뷰 하나만 붙이면 된다고 생각하지만 그렇게 되면 레이아웃 조정이 불가능해지므로 따라서 각각 하나하나 붙여야 한다.


예를들어.. 내가 삽질을 하게 된 이유이지만, 네비게이션뷰에 드롭다운 메뉴를 만들고 드롭다운 메뉴에서 선택된 항목으로 인하여 라벨크기가 가변적으로 변해야 한다면 라벨, 버튼, 이미지 각각 따로 붙여야 한다.








Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

iOS6부터 새로 추가된 AutoLayout 기능은 너무 편리하다. (사실... iOS6에서는 인터페이스가 엉망이라 불편했는데 iOS7부터는 많이 개선되었다.)


본론으로 들어가서, 스토리보드에AutoLayout을 적용하면 동적으로 프레임 좌표변경이 불가능하다.

정확히 말하면 분명 메모리상에서는 이미 바뀌어 있지만 화면에 표시될땐 변경된 좌표로 표시되지 않는 현상이 발생한다.

그 뿐만아니라 layer에 회전을 시키는 것도 제대로 동작하지 않는다.


지금까지 해결방법으로 스토리보드에서 AutoLayout를 해제하여 해결해왔는데 (정확히 말하면 AutoLayout사용을 포기한거임) 이제서야 제대로 된 해결방법을 알아냈다.



그 방법이란 좌표를 변경할 뷰를 일단 한번 띄어냈다가 다시붙이는 방법이다. 그렇게 하면 그부분만 AutoLayout의 쓸데없는 방해를 피할 수 있다.


예를들어..


for (UIView *v in moveView.subviews) {

        

        if ([v isKindOfClass:[UIView class]] == YES) {

            //변경을 원하지 않으면 패스

            continue;

        }

        

        [v removeFromSuperview];

        

        //프레임을 변경

        v.frame = CGRectMake(0, 0, 100, 200);

        

        [self.view addSubview:v];

        

    }



대충 이런식의 코딩이 가능하다.

참고로 뷰를 띄었다가 붙였을경우 IBOutlet에 연결된 커넥션이 무효화될까 걱정했었는데 무사히 동작한다.


혹시 모를 AutoLayout 때문에 고생하시는 개발자분들은 참고하시길.


2013.03.22 추가내용.

AutoLayout적용한 컨트롤러에서 UIScollView(스크롤뷰)에서 컨텐츠사이즈가 적용안되는문제가 발생하는데 아래와 같은 방법으로 해결할 수있다.


- (void)viewDidLayoutSubviews {

    

    [super viewDidLayoutSubviews];

    

    // 스코롤 컨텐츠사이즈 변경

    [scView setContentSize:CGSizeMake((_rView.frame.size.width + _tView.frame.size.width),

                                      [UIScreen mainScreen].bounds.size.height)];

    

    [self.view layoutSubviews]; //이부분이 중요

}



Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

개인적으로 iOS개발할때 불편하다고 생각되는것 중 하나가 CoreData를 이용한 개발이다.

간단한 테이블뷰어정도는 제공해줘도 될듯한데 XCode에 포함시키지 않았을까? 

애플의 사소한 배려가 없는 상황에서 지금까지는 그때끄때 무식하게 코딩으로 직접 만든 나만의 프로그램으로 해결하곤 했다. 

뭐 테이블뷰로 대충 붙여놓은 간단한 프로그램이긴 하지만.. 귀찮은 일이긴 했지만 자주 사용했으므로 나름 유용한 방법이긴했다. 

물론 앱스토어에 유용한 툴도 있겠지만 거의 파견형태로 일하는 나로서는 내 맥북도 아닌데 아무 어플리케이션이나 깔수 없었다.

그것이 무식한 방식을 선택한 이유이기도 했다.


그런데....무식하면 몸이 고생한다고... 정말 간단한 SQL만으로도 검색할 수 있는 방법이 있었다. 

sqlite명령어를 쓰면 되는데 왜 그생각을 지금까지 못했을까..


어쨋거나 반성은 나중에 하고 일단 메모해 둔다.


1. 당연한 얘기지만 확인해야할 파일을 작업폴더로 복사한 후 터미널을 실행시켜 해당 폴더로 이동한다.

2.sqlite를 실행한다. sqlite>라는 커서가 나타나면 성공.

sqlite3




3. 사용할 데이타베이스를 지정한다. (혹시 실행경로와 파일경로가 다를경우 풀패스를 적어주면 된다.)

sqlite3 데이타베이스 파일명




4. 종료할 경우 (명령어의 경우는 .(점) 으로 시작한다는 것에 주의)

sqlite>.exit 또는 .quit




5. SQL명령어는 ;를 마지막에 붙여준다.

*실제로 XCode에서 정의한 테이블명, 컬럼명 앞에 Z가 붙어 있다.

select * from 테이블명;




6. 검색시 컬럼명을 같이 출력하고 싶을때에는 헤더출력설정을 ON으로 해준다.

sqlite> .headers ON



7. 줄맞춤이 필요한 경우

sqlite> .explain ON



8. 기타 필요한 명령어

#데이타베이스 접속확인

sqlite> .database

#전체 테이블명 확인

sqlite> .tables


물론 도음말은 

sqlite> .help





Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요