반응형

최근 공부를 시작하면서 최신?이라고 하긴 좀 어렵지만 실무에서 사용하지 못해왔던 여러가지들을 찾아 가며 공부를 해보고 있다 

물론 써봐야 확실히 감이 오지만 

 

오늘은 ...... 디자인 패턴? 이라고 해야 할거 같은데 MVVM과 TCA 차이점에 대해 알아 보고자 한다. 

제목과 같이 질문을 하니........ 다음과 같이 알려주었다. 

그리고 둘의 차이점에 대한 코드를 요청 하였다 

한번 더 살펴 보아야 겠다.....

좀 더 물어 봐야 겠음.

반응형
Posted by onlyTheOne
,
반응형

claude로 어떻게 공부를 할 수 있을까 궁금해서 기존에 필터 앱을 업데이트 하고자 다음과 같이 요청 하였습니다. 

"새로운 질문을 하지 swift 언어로 iOS 15 이상에서 카메라앱을 만들거야 동영상 촬영 앱이고 기능은 다음과 같아 1. 동영상 촬영 버튼을 누르면 촬영이 되고 10초뒤에 촬영이 자동 종료 되 2. 동영상 필터는 2개가 제공될거야 3. 필터는 흑백하고 카툰 필터 4. 녹화된 영상은 앱내에 저장하는데 사진앱으로 내보내기 기능을 제공 5. 녹화사에는 전면 카메라 녹화나 후면 카메라 녹화 둘중 하나만 되게 할거야 6. 고급 카메라는 PIP 기능으로 영상이 녹화되게 할거야 7. 카메라 촬영중에는 화면에서 10초 타이머가 보여지면서 촬영된 영상이 실시간으로 보이게 할거야 여기까지 "

이렇게 요청 하니 CIFilter 기반 이 나왔다.....

CIFilter로는 퍼포먼스가 안나오는걸 알고 있기에... 다시 요청 했다.

"아 미안해... metal framework로 구현해줘 CIFilter는 퍼포먼스 안나와"

다음과 같이 안내해 주었다.

코드를 보니.... 분류가 안되어 있어 재차 요청 했다.

"파일명 별로 구분좀 해줄 수 있을까?"

 

위와 같이 프로젝트 구조도 알려주었다. 

 

그래서 각각의 소스코드는 다음과 같다. 

// MARK: - Models/ShaderTypes.swift
struct Vertex {
    var position: SIMD3<Float>
    var textureCoordinate: SIMD2<Float>
}

enum FilterType {
    case normal
    case grayscale
    case cartoon
}

// MARK: - Shaders/CameraShaders.metal
"""
#include <metal_stdlib>
using namespace metal;

struct VertexIn {
    float3 position [[attribute(0)]];
    float2 textureCoordinate [[attribute(1)]];
};

struct VertexOut {
    float4 position [[position]];
    float2 textureCoordinate;
};

vertex VertexOut vertexShader(uint vertexID [[vertex_id]],
                             constant VertexIn *vertices [[buffer(0)]]) {
    VertexOut out;
    out.position = float4(vertices[vertexID].position, 1.0);
    out.textureCoordinate = vertices[vertexID].textureCoordinate;
    return out;
}

fragment float4 fragmentShader(VertexOut in [[stage_in]],
                             texture2d<float> texture [[texture(0)]],
                             constant int &filterType [[buffer(1)]]) {
    constexpr sampler textureSampler(mag_filter::linear,
                                   min_filter::linear);
    
    float4 color = texture.sample(textureSampler, in.textureCoordinate);
    
    // Normal
    if (filterType == 0) {
        return color;
    }
    // Grayscale
    else if (filterType == 1) {
        float gray = dot(color.rgb, float3(0.299, 0.587, 0.114));
        return float4(gray, gray, gray, color.a);
    }
    // Cartoon
    else if (filterType == 2) {
        float3 original = color.rgb;
        
        float2 texelSize = float2(1.0 / texture.get_width(),
                                1.0 / texture.get_height());
        
        float3 edge = float3(0.0);
        for(int i = -1; i <= 1; i++) {
            for(int j = -1; j <= 1; j++) {
                float2 offset = float2(float(i), float(j)) * texelSize;
                float3 sample = texture.sample(textureSampler,
                                            in.textureCoordinate + offset).rgb;
                edge += abs(sample - original);
            }
        }
        
        float3 quantized = floor(original * 5.0) / 5.0;
        float edgeMask = 1.0 - smoothstep(0.2, 0.3, length(edge));
        return float4(quantized * edgeMask, color.a);
    }
    
    return color;
}
"""

// MARK: - Controllers/MetalCameraController.swift
class MetalCameraController: NSObject {
    private var captureSession: AVCaptureSession?
    private var videoOutput: AVCaptureMovieFileOutput?
    private var textureCache: CVMetalTextureCache?
    
    private let metalDevice: MTLDevice
    private let metalCommandQueue: MTLCommandQueue
    private let pipelineState: MTLRenderPipelineState
    private let vertexBuffer: MTLBuffer
    
    private var currentFilterType: FilterType = .normal
    
    private let vertices: [Vertex] = [
        Vertex(position: SIMD3<Float>(-1, -1, 0), textureCoordinate: SIMD2<Float>(0, 1)),
        Vertex(position: SIMD3<Float>(1, -1, 0), textureCoordinate: SIMD2<Float>(1, 1)),
        Vertex(position: SIMD3<Float>(-1, 1, 0), textureCoordinate: SIMD2<Float>(0, 0)),
        Vertex(position: SIMD3<Float>(1, 1, 0), textureCoordinate: SIMD2<Float>(1, 0))
    ]
    
    override init() {
        // ... (이전 코드와 동일)
    }
    
    private func setupTextureCache() {
        // ... (이전 코드와 동일)
    }
}

// MARK: - Views/Components/CameraView.swift
class CameraView: MTKView {
    var renderDelegate: CameraRenderDelegate?
    
    override init(frame frameRect: CGRect, device: MTLDevice?) {
        super.init(frame: frameRect, device: device)
        setupView()
    }
    
    required init(coder: NSCoder) {
        super.init(coder: coder)
        setupView()
    }
    
    private func setupView() {
        self.device = MTLCreateSystemDefaultDevice()
        self.backgroundColor = .black
        self.framebufferOnly = false
    }
}

// MARK: - Views/Components/CameraControls.swift
class CameraControls: UIView {
    var onRecordTap: (() -> Void)?
    var onFilterTap: (() -> Void)?
    
    private lazy var recordButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("녹화", for: .normal)
        button.backgroundColor = .red
        button.layer.cornerRadius = 25
        button.addTarget(self, action: #selector(recordButtonTapped), for: .touchUpInside)
        return button
    }()
    
    private lazy var filterButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("필터 변경", for: .normal)
        button.addTarget(self, action: #selector(filterButtonTapped), for: .touchUpInside)
        return button
    }()
    
    private lazy var timerLabel: UILabel = {
        let label = UILabel()
        label.textColor = .white
        label.font = .systemFont(ofSize: 24, weight: .bold)
        label.text = "10"
        label.textAlignment = .center
        return label
    }()
    
    @objc private func recordButtonTapped() {
        onRecordTap?()
    }
    
    @objc private func filterButtonTapped() {
        onFilterTap?()
    }
    
    // ... Layout 코드
}

// MARK: - ViewControllers/MetalCameraViewController.swift
class MetalCameraViewController: UIViewController {
    private var cameraView: CameraView!
    private var cameraControls: CameraControls!
    private var cameraController: MetalCameraController!
    
    private var currentFilter: FilterType = .normal
    private var recordingTimer: Timer?
    private var remainingTime: Int = 10
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        setupCameraController()
        setupBindings()
    }
    
    private func setupViews() {
        cameraView = CameraView(frame: view.bounds, device: MTLCreateSystemDefaultDevice())
        cameraControls = CameraControls()
        
        view.addSubview(cameraView)
        view.addSubview(cameraControls)
        
        // Layout constraints
    }
    
    private func setupCameraController() {
        cameraController = MetalCameraController()
    }
    
    private func setupBindings() {
        cameraControls.onRecordTap = { [weak self] in
            self?.handleRecordTap()
        }
        
        cameraControls.onFilterTap = { [weak self] in
            self?.handleFilterTap()
        }
    }
    
    private func handleRecordTap() {
        if recordingTimer == nil {
            startRecording()
        } else {
            stopRecording()
        }
    }
    
    private func handleFilterTap() {
        switch currentFilter {
        case .normal: currentFilter = .grayscale
        case .grayscale: currentFilter = .cartoon
        case .cartoon: currentFilter = .normal
        }
        // Update filter in camera controller
    }
    
    // ... Recording and timer related methods
}

// MARK: - Utils/VideoExporter.swift
class VideoExporter {
    static func exportVideo(at url: URL, completion: @escaping (Result<URL, Error>) -> Void) {
        // Video export implementation
    }
}

// MARK: - Protocols/CameraRenderDelegate.swift
protocol CameraRenderDelegate: AnyObject {
    func renderFrame()
    func updateFilter(_ type: FilterType)
}

여기까지...  

직접 코드를 나눠어 넣었을때.... MetalCameraViewController.swift 파일에 먼가 이상함을 감지했다. 

to be continue...

반응형
Posted by onlyTheOne
,
반응형

지난 편에 이어서...  (링크 : https://opendev4u.tistory.com/181 )

최초에 질문 했을때 combine 사용한 예시 코드를 설명해 주었다. 

그래서 다음과 같이 다시 물어보았다.

"그럼 mvvm 구현을 위해 combine을 써야 한다고 말해줘야 하는거 아닌가?" 

라고 물었더니 다음과 같이 피드백을 주었다. 

다음과 같이 피드백을 주었다. 

MVVM 을 구현 한 소스코드 예시는 크게 3가지 방법을 알려주었다. 

Combine 없이 구현 하는 방법 / Combine 이용하는 방법 / RxSwift 이용하는 방법 

그리고 부가 설명을 해주었다. 

과연 이것만 가지고 이해가 될까?? 아직 부족해 보이고... 아하 하기엔 애매한 면이 있어

더 공부 해 볼겸 해서 좀 더 물어보면서 파보기로 했다.

#3 에 이어서...

반응형
Posted by onlyTheOne
,
반응형

지난번에 이어 업데이트 하기로 한 사항을 생각 났을때 올려드립니다.

먼저 며칠전 제가 지인분께 들은 바로는 iOS 최신 버전을 업데이트 한 경우

Xcode 버전을 올려야지만 break point가 걸린다고 합니다. OS X 라이언의 Xcode에서만

가능하다는 이야기를 들었습니다. 하여 해당 부분에 대해서는

제가 보유한 테스트 시료에서 iOS 버전업을 시킬 수 없는 사항인 관계로

확인은 아마 검색 결과 링크로 대체 하게 될것 같습니다.

 

그리고 iOS 5.0이 설치된 단말을 이용 Xcode 3.2.4또는 3.2.6에서 단말 디버깅시에

break point가 안걸릴때에 대한 대처 방안입니다.

이경우에는 다음과 같이 하시면 성공 가능성이 80% 정도 보장됩니다.

1. Xcode 4를 추가로 설치(기존 Xcode에 덮어 쓰면 안됩니다.)

2. 기존 Xcode 및 새로 설치한 Xcode Organizer에서 등록된 디바이스 모두 제거

3. Xcode 3.2.X대의 Xcode 완전 종료

창에 x표시 누른다고 완전 종료가 아닙니다, 하단에 Xcode아이콘 이나 메뉴에서

종료 버튼을 누르셔야 합니다.)

4. Xcode 4.x 실행하여 디바이스 organizer에 등록

5. 이후 Xcode 4종료 후 다시 Xcode 3.x 시작

6. Xcode 3.x의 Organizer 에서 장치 연결 확인

그래도 안되신다면 위에서 3항까지 하신다음

XCode4에서 먼저 break point 걸리는지 테스트 하신다음

Xcode 3.x에서 진행 하시면 될거 같습니다.

 

보다 자세한 사항은 수일내로 추가 업데이트 하겠습니다.

반응형
Posted by onlyTheOne
,
반응형
오늘은 iOS 5에서 단말 디버깅


즉 브레이크 포인트가 안걸리는 상황에

대한 대처 방법에 대해서 적어 보려고 한다.

내가 성공한 방법이며 다른 분들이 동일 상황에서 될지는

보증 할 수 없다.... (이 방법으로 3명의 이슈를 수정했다.)

자세한 내용은 오늘 중으로 정리되서 올라갈 예정이...다
반응형
Posted by onlyTheOne
,
반응형
글쓰다가 키보드 배치가 달라서 날려 먹고 다시 쓴다. (이래서 키보드는 맥용 키보드만 써야 하나 보다 ....

iOS 앱을 단말 즉 아이폰 또는 아이패드(이하 아이팟 터치)에 올리고 테스트 하거나 앱스토어 배포 하고 나면

크래쉬 로그를 입수 하게 될 것이다.

여기서 크래쉬 로그란.... (모르는 분을 위해서)
-> 앱이 죽을때 내가 무얼 하다 죽었다 라고 유언장 같은 걸 남긴다 이게 바로 크래쉬 로그 이다.

이 크래쉬 로그를 열어 보면 메모리 주소랑 이것저것 만 보여진다.

아무것도 알 수 없다. 이때 dSYM 파일이라 불리우는 심볼릭 관련 파일을 통해

앱의 유언장을 앱 개발자가 알아 볼 수 있게 된다.

여기서 잠깐 dSYM이 어디있나요?(라고 물어 보시는 분이 있을거 같아 남긴다.)
-> dSYM은 디바이스 빌드하여 나온 결과물 *.app 파일과 같은 폴더내에 존재 한다.
이 dSYM 파일은 빌드시마다 바뀌니 배포나 테스트 목적으로 .app 파일을 만들 경우
dSYM 파일도 같이 복사하여 사본을 만들어서 따로 저장해야 한다.

다음으로 할 것은 다음과 같다.

dSYM이 존재한다면 crash 로그를 xcode의 organizer 를 통해 확인을 할 수 있다.

통상 이렇게 보여진다.
출처 : http://stackoverflow.com/questions/6086201/need-help-about-ios-crash-log
Date/Time:       2011-05-22 11:28:40.514 +0700
OS
Version:      iPhone OS 4.3.3 (8J2)
Report Version:  104

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0xf039cde6
Crashed Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libobjc.A.dylib                 0x32da1c98 objc_msgSend + 16
1   iPORTALs                        0x000801a0 0x1000 + 520608
2   iPORTALs                        0x00080930 0x1000 + 522544
3   iPORTALs                        0x0006eb0a 0x1000 + 449290

자 여기서 APP은 iPORTALs 라는 앱이다.  0xOOOOOO~ 이렇게 보이는게 메모리 주소다
dSYM을 통해 심볼릭이 된다면 저 구간이 소스파일의 어디 함수 인지 보여준다.

그러나 안보일때가 있다 이때는 다음과 같은 방법을 써야 한다.

위와 같이 crash 로그 파일을 열어 두고

app파일이 있는 디렉토리 경로로 터미널을 이용해서 접근한다. (설마 리눅스 또는 유닉스를 안써본건 아니시죠? ^^:)

접근하고 다음과 같은 명령어를 입력 하면

크래쉬 로그의 모든 내용이 심볼릭 된 결과는 아니지만

최소한의 심볼릭 된 결과를 볼 수 있다.

명령어는 다음과 같다
atos -arch arm7 -o "App명.app/App명" 메모리주소(0xOOOOOOO?)

을 입력하면 해당 메모리의 소스 파일 및 메소드 명을 알아 낼 수 있다.

정확도는.... 내가 테스트 한거 기준으로는 꽤 높았다. (죽는 케이스도 몇 번 잡았다.)

끝~
반응형
Posted by onlyTheOne
,
반응형

iOS Memory leak 에 대해서 자료 검색 도중 알게된 정보

Leak memory와 함께 존재하는 것인데 Heap에 남아 있는 거라 한다

아직 잘 이해를 못했지만 이 abandoned memory를 잡아 낼 수 있다고 한다.

혹시 leak 같지는 않지만 메모리가 의심된다면 한번쯤 찾아 보는 것도 나쁘지 않다고 생각된다.

아래는 관련된 링크 이다.

http://taehoonkoo.tistory.com/175

http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/
반응형
Posted by onlyTheOne
,